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) : null, false);
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 true} if 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 true, false, this);
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