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
26 package java.io;
27
28 import java.nio.channels.FileChannel;
29 import jdk.internal.misc.SharedSecrets;
30 import jdk.internal.misc.JavaIOFileDescriptorAccess;
31 import 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  * @apiNote
48  * To release resources used by this stream {@link #close} should be called
49  * directly or by try-with-resources. Subclasses are responsible for the cleanup
50  * of resources acquired by the subclass.
51  * Subclasses that override {@link #finalize} in order to perform cleanup
52  * should be modified to use alternative cleanup mechanisms such as
53  * {@link java.lang.ref.Cleaner} and remove the overriding {@code finalize} method.
54  *
55  * @implSpec
56  * If this FileOutputStream has been subclassed and the {@link #close}
57  * method has been overridden, the {@link #close} method will be
58  * called when the FileInputStream is unreachable.
59  * Otherwise, it is implementation specific how the resource cleanup described in
60  * {@link #close} is performed.
61  *
62  * @author  Arthur van Hoff
63  * @see     java.io.File
64  * @see     java.io.FileDescriptor
65  * @see     java.io.FileInputStream
66  * @see     java.nio.file.Files#newOutputStream
67  * @since   1.0
68  */

69 public
70 class FileOutputStream extends OutputStream
71 {
72     /**
73      * Access to FileDescriptor internals.
74      */

75     private static final JavaIOFileDescriptorAccess fdAccess =
76         SharedSecrets.getJavaIOFileDescriptorAccess();
77
78     /**
79      * The system dependent file descriptor.
80      */

81     private final FileDescriptor fd;
82
83     /**
84      * The associated channel, initialized lazily.
85      */

86     private volatile FileChannel channel;
87
88     /**
89      * The path of the referenced file
90      * (null if the stream is created with a file descriptor)
91      */

92     private final String path;
93
94     private final Object closeLock = new Object();
95
96     private volatile boolean closed;
97
98     private final Object altFinalizer;
99
100     /**
101      * Creates a file output stream to write to the file with the
102      * specified name. A new <code>FileDescriptor</code> object is
103      * created to represent this file connection.
104      * <p>
105      * First, if there is a security manager, its <code>checkWrite</code>
106      * method is called with <code>name</code> as its argument.
107      * <p>
108      * If the file exists but is a directory rather than a regular file, does
109      * not exist but cannot be created, or cannot be opened for any other
110      * reason then a <code>FileNotFoundException</code> is thrown.
111      *
112      * @implSpec Invoking this constructor with the parameter {@code name} is
113      * equivalent to invoking {@link #FileOutputStream(String,boolean)
114      * new FileOutputStream(name, false)}.
115      *
116      * @param      name   the system-dependent filename
117      * @exception  FileNotFoundException  if the file exists but is a directory
118      *                   rather than a regular file, does not exist but cannot
119      *                   be created, or cannot be opened for any other reason
120      * @exception  SecurityException  if a security manager exists and its
121      *               <code>checkWrite</code> method denies write access
122      *               to the file.
123      * @see        java.lang.SecurityManager#checkWrite(java.lang.String)
124      */

125     public FileOutputStream(String name) throws FileNotFoundException {
126         this(name != null ? new File(name) : nullfalse);
127     }
128
129     /**
130      * Creates a file output stream to write to the file with the specified
131      * name.  If the second argument is <code>true</code>, then
132      * bytes will be written to the end of the file rather than the beginning.
133      * A new <code>FileDescriptor</code> object is created to represent this
134      * file connection.
135      * <p>
136      * First, if there is a security manager, its <code>checkWrite</code>
137      * method is called with <code>name</code> as its argument.
138      * <p>
139      * If the file exists but is a directory rather than a regular file, does
140      * not exist but cannot be created, or cannot be opened for any other
141      * reason then a <code>FileNotFoundException</code> is thrown.
142      *
143      * @param     name        the system-dependent file name
144      * @param     append      if <code>true</code>, then bytes will be written
145      *                   to the end of the file rather than the beginning
146      * @exception  FileNotFoundException  if the file exists but is a directory
147      *                   rather than a regular file, does not exist but cannot
148      *                   be created, or cannot be opened for any other reason.
149      * @exception  SecurityException  if a security manager exists and its
150      *               <code>checkWrite</code> method denies write access
151      *               to the file.
152      * @see        java.lang.SecurityManager#checkWrite(java.lang.String)
153      * @since     1.1
154      */

155     public FileOutputStream(String name, boolean append)
156         throws FileNotFoundException
157     {
158         this(name != null ? new File(name) : null, append);
159     }
160
161     /**
162      * Creates a file output stream to write to the file represented by
163      * the specified <code>File</code> object. A new
164      * <code>FileDescriptor</code> object is created to represent this
165      * file connection.
166      * <p>
167      * First, if there is a security manager, its <code>checkWrite</code>
168      * method is called with the path represented by the <code>file</code>
169      * argument as its argument.
170      * <p>
171      * If the file exists but is a directory rather than a regular file, does
172      * not exist but cannot be created, or cannot be opened for any other
173      * reason then a <code>FileNotFoundException</code> is thrown.
174      *
175      * @param      file               the file to be opened for writing.
176      * @exception  FileNotFoundException  if the file exists but is a directory
177      *                   rather than a regular file, does not exist but cannot
178      *                   be created, or cannot be opened for any other reason
179      * @exception  SecurityException  if a security manager exists and its
180      *               <code>checkWrite</code> method denies write access
181      *               to the file.
182      * @see        java.io.File#getPath()
183      * @see        java.lang.SecurityException
184      * @see        java.lang.SecurityManager#checkWrite(java.lang.String)
185      */

186     public FileOutputStream(File file) throws FileNotFoundException {
187         this(file, false);
188     }
189
190     /**
191      * Creates a file output stream to write to the file represented by
192      * the specified <code>File</code> object. If the second argument is
193      * <code>true</code>, then bytes will be written to the end of the file
194      * rather than the beginning. A new <code>FileDescriptor</code> object is
195      * created to represent this file connection.
196      * <p>
197      * First, if there is a security manager, its <code>checkWrite</code>
198      * method is called with the path represented by the <code>file</code>
199      * argument as its argument.
200      * <p>
201      * If the file exists but is a directory rather than a regular file, does
202      * not exist but cannot be created, or cannot be opened for any other
203      * reason then a <code>FileNotFoundException</code> is thrown.
204      *
205      * @param      file               the file to be opened for writing.
206      * @param     append      if <code>true</code>, then bytes will be written
207      *                   to the end of the file rather than the beginning
208      * @exception  FileNotFoundException  if the file exists but is a directory
209      *                   rather than a regular file, does not exist but cannot
210      *                   be created, or cannot be opened for any other reason
211      * @exception  SecurityException  if a security manager exists and its
212      *               <code>checkWrite</code> method denies write access
213      *               to the file.
214      * @see        java.io.File#getPath()
215      * @see        java.lang.SecurityException
216      * @see        java.lang.SecurityManager#checkWrite(java.lang.String)
217      * @since 1.4
218      */

219     public FileOutputStream(File file, boolean append)
220         throws FileNotFoundException
221     {
222         String name = (file != null ? file.getPath() : null);
223         SecurityManager security = System.getSecurityManager();
224         if (security != null) {
225             security.checkWrite(name);
226         }
227         if (name == null) {
228             throw new NullPointerException();
229         }
230         if (file.isInvalid()) {
231             throw new FileNotFoundException("Invalid file path");
232         }
233         this.fd = new FileDescriptor();
234         fd.attach(this);
235         this.path = name;
236
237         open(name, append);
238         altFinalizer = getFinalizer(this);
239         if (altFinalizer == null) {
240             FileCleanable.register(fd);   // open sets the fd, register the cleanup
241         }
242     }
243
244     /**
245      * Creates a file output stream to write to the specified file
246      * descriptor, which represents an existing connection to an actual
247      * file in the file system.
248      * <p>
249      * First, if there is a security manager, its <code>checkWrite</code>
250      * method is called with the file descriptor <code>fdObj</code>
251      * argument as its argument.
252      * <p>
253      * If <code>fdObj</code> is null then a <code>NullPointerException</code>
254      * is thrown.
255      * <p>
256      * This constructor does not throw an exception if <code>fdObj</code>
257      * is {@link java.io.FileDescriptor#valid() invalid}.
258      * However, if the methods are invoked on the resulting stream to attempt
259      * I/O on the stream, an <code>IOException</code> is thrown.
260      *
261      * @param      fdObj   the file descriptor to be opened for writing
262      * @exception  SecurityException  if a security manager exists and its
263      *               <code>checkWrite</code> method denies
264      *               write access to the file descriptor
265      * @see        java.lang.SecurityManager#checkWrite(java.io.FileDescriptor)
266      */

267     public FileOutputStream(FileDescriptor fdObj) {
268         SecurityManager security = System.getSecurityManager();
269         if (fdObj == null) {
270             throw new NullPointerException();
271         }
272         if (security != null) {
273             security.checkWrite(fdObj);
274         }
275         this.fd = fdObj;
276         this.path = null;
277         this.altFinalizer = null;
278
279         fd.attach(this);
280     }
281
282     /**
283      * Opens a file, with the specified name, for overwriting or appending.
284      * @param name name of file to be opened
285      * @param append whether the file is to be opened in append mode
286      */

287     private native void open0(String name, boolean append)
288         throws FileNotFoundException;
289
290     // wrap native call to allow instrumentation
291     /**
292      * Opens a file, with the specified name, for overwriting or appending.
293      * @param name name of file to be opened
294      * @param append whether the file is to be opened in append mode
295      */

296     private void open(String name, boolean append)
297         throws FileNotFoundException {
298         open0(name, append);
299     }
300
301     /**
302      * Writes the specified byte to this file output stream.
303      *
304      * @param   b   the byte to be written.
305      * @param   append   {@code trueif the write operation first
306      *     advances the position to the end of file
307      */

308     private native void write(int b, boolean append) throws IOException;
309
310     /**
311      * Writes the specified byte to this file output stream. Implements
312      * the <code>write</code> method of <code>OutputStream</code>.
313      *
314      * @param      b   the byte to be written.
315      * @exception  IOException  if an I/O error occurs.
316      */

317     public void write(int b) throws IOException {
318         write(b, fdAccess.getAppend(fd));
319     }
320
321     /**
322      * Writes a sub array as a sequence of bytes.
323      * @param b the data to be written
324      * @param off the start offset in the data
325      * @param len the number of bytes that are written
326      * @param append {@code true} to first advance the position to the
327      *     end of file
328      * @exception IOException If an I/O error has occurred.
329      */

330     private native void writeBytes(byte b[], int off, int len, boolean append)
331         throws IOException;
332
333     /**
334      * Writes <code>b.length</code> bytes from the specified byte array
335      * to this file output stream.
336      *
337      * @param      b   the data.
338      * @exception  IOException  if an I/O error occurs.
339      */

340     public void write(byte b[]) throws IOException {
341         writeBytes(b, 0, b.length, fdAccess.getAppend(fd));
342     }
343
344     /**
345      * Writes <code>len</code> bytes from the specified byte array
346      * starting at offset <code>off</code> to this file output stream.
347      *
348      * @param      b     the data.
349      * @param      off   the start offset in the data.
350      * @param      len   the number of bytes to write.
351      * @exception  IOException  if an I/O error occurs.
352      */

353     public void write(byte b[], int off, int len) throws IOException {
354         writeBytes(b, off, len, fdAccess.getAppend(fd));
355     }
356
357     /**
358      * Closes this file output stream and releases any system resources
359      * associated with this stream. This file output stream may no longer
360      * be used for writing bytes.
361      *
362      * <p> If this stream has an associated channel then the channel is closed
363      * as well.
364      *
365      * @apiNote
366      * Overriding {@link #close} to perform cleanup actions is reliable
367      * only when called directly or when called by try-with-resources.
368      * Do not depend on finalization to invoke {@code close};
369      * finalization is not reliable and is deprecated.
370      * If cleanup of native resources is needed, other mechanisms such as
371      * {@linkplain java.lang.ref.Cleaner} should be used.
372      *
373      * @exception  IOException  if an I/O error occurs.
374      *
375      * @revised 1.4
376      * @spec JSR-51
377      */

378     public void close() throws IOException {
379         if (closed) {
380             return;
381         }
382         synchronized (closeLock) {
383             if (closed) {
384                 return;
385             }
386             closed = true;
387         }
388
389         FileChannel fc = channel;
390         if (fc != null) {
391             // possible race with getChannel(), benign since
392             // FileChannel.close is final and idempotent
393             fc.close();
394         }
395
396         fd.closeAll(new Closeable() {
397             public void close() throws IOException {
398                fd.close();
399            }
400         });
401     }
402
403     /**
404      * Returns the file descriptor associated with this stream.
405      *
406      * @return  the <code>FileDescriptor</code> object that represents
407      *          the connection to the file in the file system being used
408      *          by this <code>FileOutputStream</code> object.
409      *
410      * @exception  IOException  if an I/O error occurs.
411      * @see        java.io.FileDescriptor
412      */

413      public final FileDescriptor getFD()  throws IOException {
414         if (fd != null) {
415             return fd;
416         }
417         throw new IOException();
418      }
419
420     /**
421      * Returns the unique {@link java.nio.channels.FileChannel FileChannel}
422      * object associated with this file output stream.
423      *
424      * <p> The initial {@link java.nio.channels.FileChannel#position()
425      * position} of the returned channel will be equal to the
426      * number of bytes written to the file so far unless this stream is in
427      * append mode, in which case it will be equal to the size of the file.
428      * Writing bytes to this stream will increment the channel's position
429      * accordingly.  Changing the channel's position, either explicitly or by
430      * writing, will change this stream's file position.
431      *
432      * @return  the file channel associated with this file output stream
433      *
434      * @since 1.4
435      * @spec JSR-51
436      */

437     public FileChannel getChannel() {
438         FileChannel fc = this.channel;
439         if (fc == null) {
440             synchronized (this) {
441                 fc = this.channel;
442                 if (fc == null) {
443                     this.channel = fc = FileChannelImpl.open(fd, path, false,
444                         truefalsethis);
445                     if (closed) {
446                         try {
447                             // possible race with close(), benign since
448                             // FileChannel.close is final and idempotent
449                             fc.close();
450                         } catch (IOException ioe) {
451                             throw new InternalError(ioe); // should not happen
452                         }
453                     }
454                 }
455             }
456         }
457         return fc;
458     }
459
460     /**
461      * Cleans up the connection to the file, and ensures that the
462      * {@link #close} method of this file output stream is
463      * called when there are no more references to this stream.
464      * The {@link #finalize} method does not call {@link #close} directly.
465      *
466      * @apiNote
467      * To release resources used by this stream {@link #close} should be called
468      * directly or by try-with-resources.
469      *
470      * @implSpec
471      * If this FileOutputStream has been subclassed and the {@link #close}
472      * method has been overridden, the {@link #close} method will be
473      * called when the FileOutputStream is unreachable.
474      * Otherwise, it is implementation specific how the resource cleanup described in
475      * {@link #close} is performed.
476      *
477      * @deprecated The {@code finalize} method has been deprecated and will be removed.
478      *     Subclasses that override {@code finalize} in order to perform cleanup
479      *     should be modified to use alternative cleanup mechanisms and
480      *     to remove the overriding {@code finalize} method.
481      *     When overriding the {@code finalize} method, its implementation must explicitly
482      *     ensure that {@code super.finalize()} is invoked as described in {@link Object#finalize}.
483      *     See the specification for {@link Object#finalize()} for further
484      *     information about migration options.
485      *
486      * @exception  IOException  if an I/O error occurs.
487      * @see        java.io.FileInputStream#close()
488      */

489     @Deprecated(since="9", forRemoval = true)
490     protected void finalize() throws IOException {
491     }
492
493     private static native void initIDs();
494
495     static {
496         initIDs();
497     }
498
499     /*
500      * Returns a finalizer object if the FOS needs a finalizer; otherwise null.
501      * If the FOS has a close method; it needs an AltFinalizer.
502      */

503     private static Object getFinalizer(FileOutputStream fos) {
504         Class<?> clazz = fos.getClass();
505         while (clazz != FileOutputStream.class) {
506             try {
507                 clazz.getDeclaredMethod("close");
508                 return new AltFinalizer(fos);
509             } catch (NoSuchMethodException nsme) {
510                 // ignore
511             }
512             clazz = clazz.getSuperclass();
513         }
514         return null;
515     }
516
517     /**
518      * Class to call {@code FileOutputStream.close} when finalized.
519      * If finalization of the stream is needed, an instance is created
520      * in its constructor(s).  When the set of instances
521      * related to the stream is unreachable, the AltFinalizer performs
522      * the needed call to the stream's {@code close} method.
523      */

524     static class AltFinalizer {
525         private final FileOutputStream fos;
526
527         AltFinalizer(FileOutputStream fos) {
528             this.fos = fos;
529         }
530
531         @Override
532         @SuppressWarnings("deprecation")
533         protected final void finalize() {
534             try {
535                 if (fos.fd != null) {
536                     if (fos.fd == FileDescriptor.out || fos.fd == FileDescriptor.err) {
537                         // Subclass may override flush; otherwise it is no-op
538                         fos.flush();
539                     } else {
540                         /* if fd is shared, the references in FileDescriptor
541                          * will ensure that finalizer is only called when
542                          * safe to do so. All references using the fd have
543                          * become unreachable. We can call close()
544                          */

545                         fos.close();
546                     }
547                 }
548             } catch (IOException ioe) {
549                 // ignore
550             }
551         }
552     }
553
554 }
555