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