1 /* 2 * Copyright (c) 1994, 2013, 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 java.util.concurrent.atomic.AtomicBoolean; 30 import jdk.internal.misc.SharedSecrets; 31 import jdk.internal.misc.JavaIOFileDescriptorAccess; 32 import sun.nio.ch.FileChannelImpl; 33 34 35 /** 36 * A file output stream is an output stream for writing data to a 37 * <code>File</code> or to a <code>FileDescriptor</code>. Whether or not 38 * a file is available or may be created depends upon the underlying 39 * platform. Some platforms, in particular, allow a file to be opened 40 * for writing by only one {@code FileOutputStream} (or other 41 * file-writing object) at a time. In such situations the constructors in 42 * this class will fail if the file involved is already open. 43 * 44 * <p><code>FileOutputStream</code> is meant for writing streams of raw bytes 45 * such as image data. For writing streams of characters, consider using 46 * <code>FileWriter</code>. 47 * 48 * @author Arthur van Hoff 49 * @see java.io.File 50 * @see java.io.FileDescriptor 51 * @see java.io.FileInputStream 52 * @see java.nio.file.Files#newOutputStream 53 * @since 1.0 54 */ 55 public 56 class FileOutputStream extends OutputStream 57 { 58 /** 59 * Access to FileDescriptor internals. 60 */ 61 private static final JavaIOFileDescriptorAccess fdAccess = 62 SharedSecrets.getJavaIOFileDescriptorAccess(); 63 64 /** 65 * The system dependent file descriptor. 66 */ 67 private final FileDescriptor fd; 68 69 /** 70 * The associated channel, initialized lazily. 71 */ 72 private volatile FileChannel channel; 73 74 /** 75 * The path of the referenced file 76 * (null if the stream is created with a file descriptor) 77 */ 78 private final String path; 79 80 private final AtomicBoolean closed = new AtomicBoolean(false); 81 82 private boolean direct = false; 83 84 private int pageSize = -1; 85 86 /** 87 * Creates a file output stream to write to the file with the 88 * specified name. A new <code>FileDescriptor</code> object is 89 * created to represent this file connection. 90 * <p> 91 * First, if there is a security manager, its <code>checkWrite</code> 92 * method is called with <code>name</code> as its argument. 93 * <p> 94 * If the file exists but is a directory rather than a regular file, does 95 * not exist but cannot be created, or cannot be opened for any other 96 * reason then a <code>FileNotFoundException</code> is thrown. 97 * 98 * @param name the system-dependent filename 99 * @exception FileNotFoundException if the file exists but is a directory 100 * rather than a regular file, does not exist but cannot 101 * be created, or cannot be opened for any other reason 102 * @exception SecurityException if a security manager exists and its 103 * <code>checkWrite</code> method denies write access 104 * to the file. 105 * @see java.lang.SecurityManager#checkWrite(java.lang.String) 106 */ 107 public FileOutputStream(String name) throws FileNotFoundException { 108 this(name != null ? new File(name) : null, false, false); 109 } 110 111 /** 112 * Creates a file output stream to write to the file with the specified 113 * name. If the second argument is <code>true</code>, then 114 * bytes will be written to the end of the file rather than the beginning. 115 * A new <code>FileDescriptor</code> object is created to represent this 116 * file connection. 117 * <p> 118 * First, if there is a security manager, its <code>checkWrite</code> 119 * method is called with <code>name</code> as its argument. 120 * <p> 121 * If the file exists but is a directory rather than a regular file, does 122 * not exist but cannot be created, or cannot be opened for any other 123 * reason then a <code>FileNotFoundException</code> is thrown. 124 * 125 * @param name the system-dependent file name 126 * @param append if <code>true</code>, then bytes will be written 127 * to the end of the file rather than the beginning 128 * @exception FileNotFoundException if the file exists but is a directory 129 * rather than a regular file, does not exist but cannot 130 * be created, or cannot be opened for any other reason. 131 * @exception SecurityException if a security manager exists and its 132 * <code>checkWrite</code> method denies write access 133 * to the file. 134 * @see java.lang.SecurityManager#checkWrite(java.lang.String) 135 * @since 1.1 136 */ 137 public FileOutputStream(String name, boolean append) 138 throws FileNotFoundException 139 { 140 this(name != null ? new File(name) : null, append, false); 141 } 142 143 /** 144 * Creates a file output stream to write to the file with the specified 145 * name. If the second argument is <code>true</code>, then bytes will 146 * be written to the end of the file rather than the beginning. If the 147 * third parameter is <code>true</code>, then bytes will be directly 148 * written to storage media. A new <code>FileDescriptor</code> object 149 * is created to represent this file connection. 150 * <p> 151 * First, if there is a security manager, its <code>checkWrite</code> 152 * method is called with <code>name</code> as its argument. 153 * <p> 154 * If the file exists but is a directory rather than a regular file, does 155 * not exist but cannot be created, or cannot be opened for any other 156 * reason then a <code>FileNotFoundException</code> is thrown. 157 * 158 * @param name the system-dependent file name 159 * @param append if <code>true</code>, then bytes will be written 160 * to the end of the file rather than the beginning 161 * @param direct if <code>true</code>, then bytes will be written 162 * directly to storage media 163 * @exception FileNotFoundException if the file exists but is a directory 164 * rather than a regular file, does not exist but cannot 165 * be created, or cannot be opened for any other reason. 166 * @exception SecurityException if a security manager exists and its 167 * <code>checkWrite</code> method denies write access 168 * to the file. 169 * @see java.lang.SecurityManager#checkWrite(java.lang.String) 170 * @since 1.9 171 */ 172 public FileOutputStream(String name, boolean append, boolean direct) 173 throws FileNotFoundException 174 { 175 this(name != null ? new File(name) : null, append, direct); 176 } 177 178 /** 179 * Creates a file output stream to write to the file represented by 180 * the specified <code>File</code> object. A new 181 * <code>FileDescriptor</code> object is created to represent this 182 * file connection. 183 * <p> 184 * First, if there is a security manager, its <code>checkWrite</code> 185 * method is called with the path represented by the <code>file</code> 186 * argument as its argument. 187 * <p> 188 * If the file exists but is a directory rather than a regular file, does 189 * not exist but cannot be created, or cannot be opened for any other 190 * reason then a <code>FileNotFoundException</code> is thrown. 191 * 192 * @param file the file to be opened for writing. 193 * @exception FileNotFoundException if the file exists but is a directory 194 * rather than a regular file, does not exist but cannot 195 * be created, or cannot be opened for any other reason 196 * @exception SecurityException if a security manager exists and its 197 * <code>checkWrite</code> method denies write access 198 * to the file. 199 * @see java.io.File#getPath() 200 * @see java.lang.SecurityException 201 * @see java.lang.SecurityManager#checkWrite(java.lang.String) 202 */ 203 public FileOutputStream(File file) throws FileNotFoundException { 204 this(file, false, false); 205 } 206 207 /** 208 * Creates a file output stream to write to the file represented by 209 * the specified <code>File</code> object. If the second argument is 210 * <code>true</code>, then bytes will be written to the end of the file 211 * rather than the beginning. A new <code>FileDescriptor</code> object is 212 * created to represent this file connection. 213 * <p> 214 * First, if there is a security manager, its <code>checkWrite</code> 215 * method is called with the path represented by the <code>file</code> 216 * argument as its argument. 217 * <p> 218 * If the file exists but is a directory rather than a regular file, does 219 * not exist but cannot be created, or cannot be opened for any other 220 * reason then a <code>FileNotFoundException</code> is thrown. 221 * 222 * @param file the file to be opened for writing. 223 * @param append if <code>true</code>, then bytes will be written 224 * to the end of the file rather than the beginning 225 * @exception FileNotFoundException if the file exists but is a directory 226 * rather than a regular file, does not exist but cannot 227 * be created, or cannot be opened for any other reason 228 * @exception SecurityException if a security manager exists and its 229 * <code>checkWrite</code> method denies write access 230 * to the file. 231 * @see java.io.File#getPath() 232 * @see java.lang.SecurityException 233 * @see java.lang.SecurityManager#checkWrite(java.lang.String) 234 * @since 1.4 235 */ 236 public FileOutputStream(File file, boolean append) 237 throws FileNotFoundException 238 { 239 this(file, append, false); 240 } 241 242 /** 243 * Creates a file output stream to write to the file represented by 244 * the specified <code>File</code> object. If the second argument is 245 * <code>true</code>, then bytes will be written to the end of the file 246 * rather than the beginning. If the third argument is <code>true</code>, 247 * then bytes will be directly written to storage media. A new 248 * <code>FileDescriptor</code> object is created to represent this file 249 * connection. 250 * <p> 251 * First, if there is a security manager, its <code>checkWrite</code> 252 * method is called with the path represented by the <code>file</code> 253 * argument as its argument. 254 * <p> 255 * If the file exists but is a directory rather than a regular file, does 256 * not exist but cannot be created, or cannot be opened for any other 257 * reason then a <code>FileNotFoundException</code> is thrown. 258 * 259 * @param file the file to be opened for writing. 260 * @param append if <code>true</code>, then bytes will be written 261 * to the end of the file rather than the beginning 262 * @param direct if <code>true</code>, then bytes will be written 263 * directly to storage media 264 * @exception FileNotFoundException if the file exists but is a directory 265 * rather than a regular file, does not exist but cannot 266 * be created, or cannot be opened for any other reason 267 * @exception SecurityException if a security manager exists and its 268 * <code>checkWrite</code> method denies write access 269 * to the file. 270 * @see java.io.File#getPath() 271 * @see java.lang.SecurityException 272 * @see java.lang.SecurityManager#checkWrite(java.lang.String) 273 * @since 1.9 274 */ 275 public FileOutputStream(File file, boolean append, boolean direct) 276 throws FileNotFoundException 277 { 278 this.direct = direct; 279 if (direct) { 280 getPageSize(); 281 } 282 String name = (file != null ? file.getPath() : null); 283 SecurityManager security = System.getSecurityManager(); 284 if (security != null) { 285 security.checkWrite(name); 286 } 287 if (name == null) { 288 throw new NullPointerException(); 289 } 290 if (file.isInvalid()) { 291 throw new FileNotFoundException("Invalid file path"); 292 } 293 this.fd = new FileDescriptor(); 294 fd.attach(this); 295 this.path = name; 296 297 open(name, append); 298 } 299 300 /** 301 * Creates a file output stream to write to the specified file 302 * descriptor, which represents an existing connection to an actual 303 * file in the file system. 304 * <p> 305 * First, if there is a security manager, its <code>checkWrite</code> 306 * method is called with the file descriptor <code>fdObj</code> 307 * argument as its argument. 308 * <p> 309 * If <code>fdObj</code> is null then a <code>NullPointerException</code> 310 * is thrown. 311 * <p> 312 * This constructor does not throw an exception if <code>fdObj</code> 313 * is {@link java.io.FileDescriptor#valid() invalid}. 314 * However, if the methods are invoked on the resulting stream to attempt 315 * I/O on the stream, an <code>IOException</code> is thrown. 316 * 317 * @param fdObj the file descriptor to be opened for writing 318 * @exception SecurityException if a security manager exists and its 319 * <code>checkWrite</code> method denies 320 * write access to the file descriptor 321 * @see java.lang.SecurityManager#checkWrite(java.io.FileDescriptor) 322 */ 323 public FileOutputStream(FileDescriptor fdObj) { 324 SecurityManager security = System.getSecurityManager(); 325 if (fdObj == null) { 326 throw new NullPointerException(); 327 } 328 if (security != null) { 329 security.checkWrite(fdObj); 330 } 331 this.fd = fdObj; 332 this.path = null; 333 334 fd.attach(this); 335 } 336 337 /** 338 * Opens a file, with the specified name, for overwriting or appending. 339 * @param name name of file to be opened 340 * @param append whether the file is to be opened in append mode 341 * @param direct whether the file s to be opened in the direct mode 342 */ 343 private native void open0(String name, boolean append, boolean direct) 344 throws FileNotFoundException; 345 346 // wrap native call to allow instrumentation 347 /** 348 * Opens a file, with the specified name, for overwriting or appending. 349 * @param name name of file to be opened 350 * @param append whether the file is to be opened in append mode 351 */ 352 private void open(String name, boolean append) 353 throws FileNotFoundException { 354 open0(name, append, direct); 355 } 356 357 private native int getPageSize0(); 358 359 private int getPageSize() { 360 if (pageSize == -1) { 361 pageSize = getPageSize0(); 362 } 363 return this.pageSize; 364 } 365 366 /** 367 * Writes the specified byte to this file output stream. 368 * 369 * @param b the byte to be written. 370 * @param append {@code true} if the write operation first 371 * advances the position to the end of file 372 */ 373 private native void write(int b, boolean append) throws IOException; 374 375 /** 376 * Writes the specified byte to this file output stream. Implements 377 * the <code>write</code> method of <code>OutputStream</code>. 378 * 379 * @param b the byte to be written. 380 * @exception IOException if an I/O error occurs. 381 */ 382 public void write(int b) throws IOException { 383 write(b, fdAccess.getAppend(fd)); 384 } 385 386 /** 387 * Writes a sub array as a sequence of bytes. 388 * @param b the data to be written 389 * @param off the start offset in the data 390 * @param len the number of bytes that are written 391 * @param append {@code true} to first advance the position to the 392 * end of file 393 * @exception IOException If an I/O error has occurred. 394 */ 395 private native void writeBytes(byte b[], int off, int len, boolean append) 396 throws IOException; 397 398 /** 399 * Writes a sub array as a sequence of bytes with DirectIO. 400 * @param b the data to be written 401 * @param off the start offset in the data 402 * @param len the number of bytes that are written 403 * @param append {@code true} to first advance the position to the 404 * end of file 405 * @exception IOException If an I/O error has occurred.*/ 406 private native void writeBytesD(byte b[], int off, int len, boolean append) 407 throws IOException; 408 409 /** 410 * Writes <code>b.length</code> bytes from the specified byte array 411 * to this file output stream. 412 * 413 * @param b the data. 414 * @exception IOException if an I/O error occurs. 415 */ 416 public void write(byte b[]) throws IOException { 417 if (direct) { 418 if((b.length % pageSize != 0) ||(getCurrentLocation() % pageSize != 0)) { 419 throw new IOException("In DirectIO mode, the IO size must be aligned " 420 + "with kernel page size " + pageSize + " bytes!"); 421 } 422 writeBytesD(b, 0, b.length, fdAccess.getAppend(fd)); 423 } else { 424 writeBytes(b, 0, b.length, fdAccess.getAppend(fd)); 425 } 426 } 427 428 /** 429 * Writes <code>len</code> bytes from the specified byte array 430 * starting at offset <code>off</code> to this file output stream. 431 * 432 * @param b the data. 433 * @param off the start offset in the data. 434 * @param len the number of bytes to write. 435 * @exception IOException if an I/O error occurs. 436 */ 437 public void write(byte b[], int off, int len) throws IOException { 438 if (direct) { 439 if((len % pageSize != 0) || (getCurrentLocation() % pageSize != 0)) { 440 throw new IOException("In DirectIO mode, the IO size and the start " 441 + "point must be aligned with kernel page size " + pageSize + " bytes!"); 442 } 443 writeBytesD(b, off, len, fdAccess.getAppend(fd)); 444 } else { 445 writeBytes(b, off, len, fdAccess.getAppend(fd)); 446 } 447 } 448 449 private long getCurrentLocation() throws IOException { 450 return getCurrentLocation0(); 451 } 452 453 private native long getCurrentLocation0() throws IOException; 454 455 /** 456 * Closes this file output stream and releases any system resources 457 * associated with this stream. This file output stream may no longer 458 * be used for writing bytes. 459 * 460 * <p> If this stream has an associated channel then the channel is closed 461 * as well. 462 * 463 * @exception IOException if an I/O error occurs. 464 * 465 * @revised 1.4 466 * @spec JSR-51 467 */ 468 public void close() throws IOException { 469 if (!closed.compareAndSet(false, true)) { 470 // if compareAndSet() returns false closed was already true 471 return; 472 } 473 474 FileChannel fc = channel; 475 if (fc != null) { 476 fc.close(); 477 } 478 479 fd.closeAll(new Closeable() { 480 public void close() throws IOException { 481 close0(); 482 } 483 }); 484 } 485 486 /** 487 * Returns the file descriptor associated with this stream. 488 * 489 * @return the <code>FileDescriptor</code> object that represents 490 * the connection to the file in the file system being used 491 * by this <code>FileOutputStream</code> object. 492 * 493 * @exception IOException if an I/O error occurs. 494 * @see java.io.FileDescriptor 495 */ 496 public final FileDescriptor getFD() throws IOException { 497 if (fd != null) { 498 return fd; 499 } 500 throw new IOException(); 501 } 502 503 /** 504 * Returns the unique {@link java.nio.channels.FileChannel FileChannel} 505 * object associated with this file output stream. 506 * 507 * <p> The initial {@link java.nio.channels.FileChannel#position() 508 * position} of the returned channel will be equal to the 509 * number of bytes written to the file so far unless this stream is in 510 * append mode, in which case it will be equal to the size of the file. 511 * Writing bytes to this stream will increment the channel's position 512 * accordingly. Changing the channel's position, either explicitly or by 513 * writing, will change this stream's file position. 514 * 515 * @return the file channel associated with this file output stream 516 * 517 * @since 1.4 518 * @spec JSR-51 519 */ 520 public FileChannel getChannel() { 521 FileChannel fc = this.channel; 522 if (fc == null) { 523 synchronized (this) { 524 fc = this.channel; 525 if (fc == null) { 526 this.channel = fc = FileChannelImpl.open(fd, path, false, true, this); 527 if (closed.get()) { 528 try { 529 fc.close(); 530 } catch (IOException ioe) { 531 throw new InternalError(ioe); // should not happen 532 } 533 } 534 } 535 } 536 } 537 return fc; 538 } 539 540 /** 541 * Cleans up the connection to the file, and ensures that the 542 * <code>close</code> method of this file output stream is 543 * called when there are no more references to this stream. 544 * 545 * @exception IOException if an I/O error occurs. 546 * @see java.io.FileInputStream#close() 547 */ 548 protected void finalize() throws IOException { 549 if (fd != null) { 550 if (fd == FileDescriptor.out || fd == FileDescriptor.err) { 551 flush(); 552 } else { 553 /* if fd is shared, the references in FileDescriptor 554 * will ensure that finalizer is only called when 555 * safe to do so. All references using the fd have 556 * become unreachable. We can call close() 557 */ 558 close(); 559 } 560 } 561 } 562 563 private native void close0() throws IOException; 564 565 private static native void initIDs(); 566 567 static { 568 initIDs(); 569 } 570 571 }