1 /* 2 * Copyright (c) 1994, 2023, 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.access.SharedSecrets; 30 import jdk.internal.access.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} or to a {@code FileDescriptor}. 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} is meant for writing streams of raw bytes 44 * such as image data. For writing streams of characters, consider using 45 * {@code FileWriter}. 46 * 47 * @apiNote 48 * The {@link #close} method should be called to release resources used by this 49 * stream, either directly, or with the {@code try}-with-resources statement. 50 * 51 * @implSpec 52 * Subclasses are responsible for the cleanup of resources acquired by the subclass. 53 * Subclasses requiring that resource cleanup take place after a stream becomes 54 * unreachable should use {@link java.lang.ref.Cleaner} or some other mechanism. 55 * 56 * @author Arthur van Hoff 57 * @see java.io.File 58 * @see java.io.FileDescriptor 59 * @see java.io.FileInputStream 60 * @see java.nio.file.Files#newOutputStream 61 * @since 1.0 62 */ 63 public class FileOutputStream extends OutputStream 64 { 65 /** 66 * Access to FileDescriptor internals. 67 */ 68 private static final JavaIOFileDescriptorAccess FD_ACCESS = 69 SharedSecrets.getJavaIOFileDescriptorAccess(); 70 71 /** 72 * The system dependent file descriptor. 73 */ 74 private final FileDescriptor fd; 75 76 /** 77 * The associated channel, initialized lazily. 78 */ 79 private volatile FileChannel channel; 80 81 /** 82 * The path of the referenced file 83 * (null if the stream is created with a file descriptor) 84 */ 85 private final String path; 86 87 private final Object closeLock = new Object(); 88 89 private volatile boolean closed; 90 91 /** 92 * Creates a file output stream to write to the file with the 93 * specified name. A new {@code FileDescriptor} object is 94 * created to represent this file connection. 95 * <p> 96 * First, if there is a security manager, its {@code checkWrite} 97 * method is called with {@code name} as its argument. 98 * <p> 99 * If the file exists but is a directory rather than a regular file, does 100 * not exist but cannot be created, or cannot be opened for any other 101 * reason then a {@code FileNotFoundException} is thrown. 102 * 103 * @implSpec Invoking this constructor with the parameter {@code name} is 104 * equivalent to invoking {@link #FileOutputStream(String,boolean) 105 * new FileOutputStream(name, false)}. 106 * 107 * @param name the system-dependent filename 108 * @throws FileNotFoundException if the file exists but is a directory 109 * rather than a regular file, does not exist but cannot 110 * be created, or cannot be opened for any other reason 111 * @throws SecurityException if a security manager exists and its 112 * {@code checkWrite} method denies write access 113 * to the file. 114 * @see java.lang.SecurityManager#checkWrite(java.lang.String) 115 */ 116 public FileOutputStream(String name) throws FileNotFoundException { 117 this(name != null ? new File(name) : null, false); 118 } 119 120 /** 121 * Creates a file output stream to write to the file with the specified 122 * name. If the second argument is {@code true}, then 123 * bytes will be written to the end of the file rather than the beginning. 124 * A new {@code FileDescriptor} object is created to represent this 125 * file connection. 126 * <p> 127 * First, if there is a security manager, its {@code checkWrite} 128 * method is called with {@code name} as its argument. 129 * <p> 130 * If the file exists but is a directory rather than a regular file, does 131 * not exist but cannot be created, or cannot be opened for any other 132 * reason then a {@code FileNotFoundException} is thrown. 133 * 134 * @param name the system-dependent file name 135 * @param append if {@code true}, then bytes will be written 136 * to the end of the file rather than the beginning 137 * @throws FileNotFoundException if the file exists but is a directory 138 * rather than a regular file, does not exist but cannot 139 * be created, or cannot be opened for any other reason. 140 * @throws SecurityException if a security manager exists and its 141 * {@code checkWrite} method denies write access 142 * to the file. 143 * @see java.lang.SecurityManager#checkWrite(java.lang.String) 144 * @since 1.1 145 */ 146 public FileOutputStream(String name, boolean append) 147 throws FileNotFoundException 148 { 149 this(name != null ? new File(name) : null, append); 150 } 151 152 /** 153 * Creates a file output stream to write to the file represented by 154 * the specified {@code File} object. A new 155 * {@code FileDescriptor} object is created to represent this 156 * file connection. 157 * <p> 158 * First, if there is a security manager, its {@code checkWrite} 159 * method is called with the path represented by the {@code file} 160 * argument as its argument. 161 * <p> 162 * If the file exists but is a directory rather than a regular file, does 163 * not exist but cannot be created, or cannot be opened for any other 164 * reason then a {@code FileNotFoundException} is thrown. 165 * 166 * @param file the file to be opened for writing. 167 * @throws FileNotFoundException if the file exists but is a directory 168 * rather than a regular file, does not exist but cannot 169 * be created, or cannot be opened for any other reason 170 * @throws SecurityException if a security manager exists and its 171 * {@code checkWrite} method denies write access 172 * to the file. 173 * @see java.io.File#getPath() 174 * @see java.lang.SecurityException 175 * @see java.lang.SecurityManager#checkWrite(java.lang.String) 176 */ 177 public FileOutputStream(File file) throws FileNotFoundException { 178 this(file, false); 179 } 180 181 /** 182 * Creates a file output stream to write to the file represented by 183 * the specified {@code File} object. If the second argument is 184 * {@code true}, then bytes will be written to the end of the file 185 * rather than the beginning. A new {@code FileDescriptor} object is 186 * created to represent this file connection. 187 * <p> 188 * First, if there is a security manager, its {@code checkWrite} 189 * method is called with the path represented by the {@code file} 190 * argument as its argument. 191 * <p> 192 * If the file exists but is a directory rather than a regular file, does 193 * not exist but cannot be created, or cannot be opened for any other 194 * reason then a {@code FileNotFoundException} is thrown. 195 * 196 * @param file the file to be opened for writing. 197 * @param append if {@code true}, then bytes will be written 198 * to the end of the file rather than the beginning 199 * @throws FileNotFoundException if the file exists but is a directory 200 * rather than a regular file, does not exist but cannot 201 * be created, or cannot be opened for any other reason 202 * @throws SecurityException if a security manager exists and its 203 * {@code checkWrite} method denies write access 204 * to the file. 205 * @see java.io.File#getPath() 206 * @see java.lang.SecurityException 207 * @see java.lang.SecurityManager#checkWrite(java.lang.String) 208 * @since 1.4 209 */ 210 @SuppressWarnings("this-escape") 211 public FileOutputStream(File file, boolean append) 212 throws FileNotFoundException 213 { 214 String name = (file != null ? file.getPath() : null); 215 @SuppressWarnings("removal") 216 SecurityManager security = System.getSecurityManager(); 217 if (security != null) { 218 security.checkWrite(name); 219 } 220 if (name == null) { 221 throw new NullPointerException(); 222 } 223 if (file.isInvalid()) { 224 throw new FileNotFoundException("Invalid file path"); 225 } 226 this.fd = new FileDescriptor(); 227 fd.attach(this); 228 this.path = name; 229 230 open(name, append); 231 FileCleanable.register(fd); // open sets the fd, register the cleanup 232 } 233 234 /** 235 * Creates a file output stream to write to the specified file 236 * descriptor, which represents an existing connection to an actual 237 * file in the file system. 238 * <p> 239 * First, if there is a security manager, its {@code checkWrite} 240 * method is called with the file descriptor {@code fdObj} 241 * argument as its argument. 242 * <p> 243 * If {@code fdObj} is null then a {@code NullPointerException} 244 * is thrown. 245 * <p> 246 * This constructor does not throw an exception if {@code fdObj} 247 * is {@link java.io.FileDescriptor#valid() invalid}. 248 * However, if the methods are invoked on the resulting stream to attempt 249 * I/O on the stream, an {@code IOException} is thrown. 250 * 251 * @param fdObj the file descriptor to be opened for writing 252 * @throws SecurityException if a security manager exists and its 253 * {@code checkWrite} method denies 254 * write access to the file descriptor 255 * @see java.lang.SecurityManager#checkWrite(java.io.FileDescriptor) 256 */ 257 @SuppressWarnings("this-escape") 258 public FileOutputStream(FileDescriptor fdObj) { 259 @SuppressWarnings("removal") 260 SecurityManager security = System.getSecurityManager(); 261 if (fdObj == null) { 262 throw new NullPointerException(); 263 } 264 if (security != null) { 265 security.checkWrite(fdObj); 266 } 267 this.fd = fdObj; 268 this.path = null; 269 270 fd.attach(this); 271 } 272 273 /** 274 * Opens a file, with the specified name, for overwriting or appending. 275 * @param name name of file to be opened 276 * @param append whether the file is to be opened in append mode 277 */ 278 private native void open0(String name, boolean append) 279 throws FileNotFoundException; 280 281 // wrap native call to allow instrumentation 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 void open(String name, boolean append) throws FileNotFoundException { 288 open0(name, append); 289 } 290 291 /** 292 * Writes the specified byte to this file output stream. 293 * 294 * @param b the byte to be written. 295 * @param append {@code true} if the write operation first 296 * advances the position to the end of file 297 */ 298 private native void write(int b, boolean append) throws IOException; 299 300 /** 301 * Writes the specified byte to this file output stream. Implements 302 * the {@code write} method of {@code OutputStream}. 303 * 304 * @param b the byte to be written. 305 * @throws IOException if an I/O error occurs. 306 */ 307 @Override 308 public void write(int b) throws IOException { 309 boolean append = FD_ACCESS.getAppend(fd); 310 write(b, append); 311 } 312 313 /** 314 * Writes a sub array as a sequence of bytes. 315 * @param b the data to be written 316 * @param off the start offset in the data 317 * @param len the number of bytes that are written 318 * @param append {@code true} to first advance the position to the 319 * end of file 320 * @throws IOException If an I/O error has occurred. 321 */ 322 private native void writeBytes(byte[] b, int off, int len, boolean append) 323 throws IOException; 324 325 /** 326 * Writes {@code b.length} bytes from the specified byte array 327 * to this file output stream. 328 * 329 * @param b {@inheritDoc} 330 * @throws IOException {@inheritDoc} 331 */ 332 @Override 333 public void write(byte[] b) throws IOException { 334 boolean append = FD_ACCESS.getAppend(fd); 335 writeBytes(b, 0, b.length, append); 336 } 337 338 /** 339 * Writes {@code len} bytes from the specified byte array 340 * starting at offset {@code off} to this file output stream. 341 * 342 * @param b {@inheritDoc} 343 * @param off {@inheritDoc} 344 * @param len {@inheritDoc} 345 * @throws IOException if an I/O error occurs. 346 * @throws IndexOutOfBoundsException {@inheritDoc} 347 */ 348 @Override 349 public void write(byte[] b, int off, int len) throws IOException { 350 boolean append = FD_ACCESS.getAppend(fd); 351 writeBytes(b, off, len, append); 352 } 353 354 /** 355 * Closes this file output stream and releases any system resources 356 * associated with this stream. This file output stream may no longer 357 * be used for writing bytes. 358 * 359 * <p> If this stream has an associated channel then the channel is closed 360 * as well. 361 * 362 * @apiNote 363 * Overriding {@link #close} to perform cleanup actions is reliable 364 * only when called directly or when called by try-with-resources. 365 * 366 * @implSpec 367 * Subclasses requiring that resource cleanup take place after a stream becomes 368 * unreachable should use the {@link java.lang.ref.Cleaner} mechanism. 369 * 370 * <p> 371 * If this stream has an associated channel then this method will close the 372 * channel, which in turn will close this stream. Subclasses that override 373 * this method should be prepared to handle possible reentrant invocation. 374 * 375 * @throws IOException if an I/O error occurs. 376 */ 377 @Override 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} object that represents 407 * the connection to the file in the file system being used 408 * by this {@code FileOutputStream} object. 409 * 410 * @throws 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 */ 436 public FileChannel getChannel() { 437 FileChannel fc = this.channel; 438 if (fc == null) { 439 synchronized (this) { 440 fc = this.channel; 441 if (fc == null) { 442 fc = FileChannelImpl.open(fd, path, false, true, false, false, this); 443 this.channel = fc; 444 if (closed) { 445 try { 446 // possible race with close(), benign since 447 // FileChannel.close is final and idempotent 448 fc.close(); 449 } catch (IOException ioe) { 450 throw new InternalError(ioe); // should not happen 451 } 452 } 453 } 454 } 455 } 456 return fc; 457 } 458 459 private static native void initIDs(); 460 461 static { 462 initIDs(); 463 } 464 }