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 }