1 /* 2 * Copyright (c) 1994, 2012, 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 sun.nio.ch.FileChannelImpl; 30 import sun.misc.IoTrace; 31 32 33 /** 34 * A file output stream is an output stream for writing data to a 35 * <code>File</code> or to a <code>FileDescriptor</code>. Whether or not 36 * a file is available or may be created depends upon the underlying 37 * platform. Some platforms, in particular, allow a file to be opened 38 * for writing by only one <tt>FileOutputStream</tt> (or other 39 * file-writing object) at a time. In such situations the constructors in 40 * this class will fail if the file involved is already open. 41 * 42 * <p><code>FileOutputStream</code> is meant for writing streams of raw bytes 43 * such as image data. For writing streams of characters, consider using 44 * <code>FileWriter</code>. 45 * 46 * @author Arthur van Hoff 47 * @see java.io.File 48 * @see java.io.FileDescriptor 49 * @see java.io.FileInputStream 50 * @see java.nio.file.Files#newOutputStream 51 * @since JDK1.0 52 */ 53 public 54 class FileOutputStream extends OutputStream 55 { 56 /** 57 * The system dependent file descriptor. 58 */ 59 private final FileDescriptor fd; 60 61 /** 62 * The path of the referenced file (null if the stream is created with a file descriptor) 63 */ 64 private final String path; 65 66 /** 67 * True if the file is opened for append. 68 */ 69 private final boolean append; 70 71 /** 72 * The associated channel, initalized lazily. 73 */ 74 private FileChannel channel; 75 76 private final Object closeLock = new Object(); 77 private volatile boolean closed = false; 78 private static final ThreadLocal<Boolean> runningFinalize = 79 new ThreadLocal<>(); 80 81 private static boolean isRunningFinalize() { 82 Boolean val; 83 if ((val = runningFinalize.get()) != null) 84 return val.booleanValue(); 85 return false; 86 } 87 88 /** 89 * Creates a file output stream to write to the file with the 90 * specified name. A new <code>FileDescriptor</code> object is 91 * created to represent this file connection. 92 * <p> 93 * First, if there is a security manager, its <code>checkWrite</code> 94 * method is called with <code>name</code> as its argument. 95 * <p> 96 * If the file exists but is a directory rather than a regular file, does 97 * not exist but cannot be created, or cannot be opened for any other 98 * reason then a <code>FileNotFoundException</code> is thrown. 99 * 100 * @param name the system-dependent filename 101 * @exception FileNotFoundException if the file exists but is a directory 102 * rather than a regular file, does not exist but cannot 103 * be created, or cannot be opened for any other reason 104 * @exception SecurityException if a security manager exists and its 105 * <code>checkWrite</code> method denies write access 106 * to the file. 107 * @see java.lang.SecurityManager#checkWrite(java.lang.String) 108 */ 109 public FileOutputStream(String name) throws FileNotFoundException { 110 this(name != null ? new File(name) : null, false); 111 } 112 113 /** 114 * Creates a file output stream to write to the file with the specified 115 * name. If the second argument is <code>true</code>, then 116 * bytes will be written to the end of the file rather than the beginning. 117 * A new <code>FileDescriptor</code> object is created to represent this 118 * file connection. 119 * <p> 120 * First, if there is a security manager, its <code>checkWrite</code> 121 * method is called with <code>name</code> as its argument. 122 * <p> 123 * If the file exists but is a directory rather than a regular file, does 124 * not exist but cannot be created, or cannot be opened for any other 125 * reason then a <code>FileNotFoundException</code> is thrown. 126 * 127 * @param name the system-dependent file name 128 * @param append if <code>true</code>, then bytes will be written 129 * to the end of the file rather than the beginning 130 * @exception FileNotFoundException if the file exists but is a directory 131 * rather than a regular file, does not exist but cannot 132 * be created, or cannot be opened for any other reason. 133 * @exception SecurityException if a security manager exists and its 134 * <code>checkWrite</code> method denies write access 135 * to the file. 136 * @see java.lang.SecurityManager#checkWrite(java.lang.String) 137 * @since JDK1.1 138 */ 139 public FileOutputStream(String name, boolean append) 140 throws FileNotFoundException 141 { 142 this(name != null ? new File(name) : null, append); 143 } 144 145 /** 146 * Creates a file output stream to write to the file represented by 147 * the specified <code>File</code> object. A new 148 * <code>FileDescriptor</code> object is created to represent this 149 * file connection. 150 * <p> 151 * First, if there is a security manager, its <code>checkWrite</code> 152 * method is called with the path represented by the <code>file</code> 153 * argument as its argument. 154 * <p> 155 * If the file exists but is a directory rather than a regular file, does 156 * not exist but cannot be created, or cannot be opened for any other 157 * reason then a <code>FileNotFoundException</code> is thrown. 158 * 159 * @param file the file to be opened for writing. 160 * @exception FileNotFoundException if the file exists but is a directory 161 * rather than a regular file, does not exist but cannot 162 * be created, or cannot be opened for any other reason 163 * @exception SecurityException if a security manager exists and its 164 * <code>checkWrite</code> method denies write access 165 * to the file. 166 * @see java.io.File#getPath() 167 * @see java.lang.SecurityException 168 * @see java.lang.SecurityManager#checkWrite(java.lang.String) 169 */ 170 public FileOutputStream(File file) throws FileNotFoundException { 171 this(file, false); 172 } 173 174 /** 175 * Creates a file output stream to write to the file represented by 176 * the specified <code>File</code> object. If the second argument is 177 * <code>true</code>, then bytes will be written to the end of the file 178 * rather than the beginning. A new <code>FileDescriptor</code> object is 179 * created to represent this file connection. 180 * <p> 181 * First, if there is a security manager, its <code>checkWrite</code> 182 * method is called with the path represented by the <code>file</code> 183 * argument as its argument. 184 * <p> 185 * If the file exists but is a directory rather than a regular file, does 186 * not exist but cannot be created, or cannot be opened for any other 187 * reason then a <code>FileNotFoundException</code> is thrown. 188 * 189 * @param file the file to be opened for writing. 190 * @param append if <code>true</code>, then bytes will be written 191 * to the end of the file rather than the beginning 192 * @exception FileNotFoundException if the file exists but is a directory 193 * rather than a regular file, does not exist but cannot 194 * be created, or cannot be opened for any other reason 195 * @exception SecurityException if a security manager exists and its 196 * <code>checkWrite</code> method denies write access 197 * to the file. 198 * @see java.io.File#getPath() 199 * @see java.lang.SecurityException 200 * @see java.lang.SecurityManager#checkWrite(java.lang.String) 201 * @since 1.4 202 */ 203 public FileOutputStream(File file, boolean append) 204 throws FileNotFoundException 205 { 206 String name = (file != null ? file.getPath() : null); 207 SecurityManager security = System.getSecurityManager(); 208 if (security != null) { 209 security.checkWrite(name); 210 } 211 if (name == null) { 212 throw new NullPointerException(); 213 } 214 this.fd = new FileDescriptor(); 215 this.append = append; 216 this.path = name; 217 fd.incrementAndGetUseCount(); 218 open(name, append); 219 } 220 221 /** 222 * Creates a file output stream to write to the specified file 223 * descriptor, which represents an existing connection to an actual 224 * file in the file system. 225 * <p> 226 * First, if there is a security manager, its <code>checkWrite</code> 227 * method is called with the file descriptor <code>fdObj</code> 228 * argument as its argument. 229 * <p> 230 * If <code>fdObj</code> is null then a <code>NullPointerException</code> 231 * is thrown. 232 * <p> 233 * This constructor does not throw an exception if <code>fdObj</code> 234 * is {@link java.io.FileDescriptor#valid() invalid}. 235 * However, if the methods are invoked on the resulting stream to attempt 236 * I/O on the stream, an <code>IOException</code> is thrown. 237 * 238 * @param fdObj the file descriptor to be opened for writing 239 * @exception SecurityException if a security manager exists and its 240 * <code>checkWrite</code> method denies 241 * write access to the file descriptor 242 * @see java.lang.SecurityManager#checkWrite(java.io.FileDescriptor) 243 */ 244 public FileOutputStream(FileDescriptor fdObj) { 245 SecurityManager security = System.getSecurityManager(); 246 if (fdObj == null) { 247 throw new NullPointerException(); 248 } 249 if (security != null) { 250 security.checkWrite(fdObj); 251 } 252 this.fd = fdObj; 253 this.path = null; 254 this.append = false; 255 256 /* 257 * FileDescriptor is being shared by streams. 258 * Ensure that it's GC'ed only when all the streams/channels are done 259 * using it. 260 */ 261 fd.incrementAndGetUseCount(); 262 } 263 264 /** 265 * Opens a file, with the specified name, for overwriting or appending. 266 * @param name name of file to be opened 267 * @param append whether the file is to be opened in append mode 268 */ 269 private native void open(String name, boolean append) 270 throws FileNotFoundException; 271 272 /** 273 * Writes the specified byte to this file output stream. 274 * 275 * @param b the byte to be written. 276 * @param append {@code true} if the write operation first 277 * advances the position to the end of file 278 */ 279 private native void write(int b, boolean append) throws IOException; 280 281 /** 282 * Writes the specified byte to this file output stream. Implements 283 * the <code>write</code> method of <code>OutputStream</code>. 284 * 285 * @param b the byte to be written. 286 * @exception IOException if an I/O error occurs. 287 */ 288 public void write(int b) throws IOException { 289 Object traceContext = IoTrace.fileWriteBegin(path); 290 int bytesWritten = 0; 291 try { 292 write(b, append); 293 bytesWritten = 1; 294 } finally { 295 IoTrace.fileWriteEnd(traceContext, bytesWritten); 296 } 297 } 298 299 /** 300 * Writes a sub array as a sequence of bytes. 301 * @param b the data to be written 302 * @param off the start offset in the data 303 * @param len the number of bytes that are written 304 * @param append {@code true} to first advance the position to the 305 * end of file 306 * @exception IOException If an I/O error has occurred. 307 */ 308 private native void writeBytes(byte b[], int off, int len, boolean append) 309 throws IOException; 310 311 /** 312 * Writes <code>b.length</code> bytes from the specified byte array 313 * to this file output stream. 314 * 315 * @param b the data. 316 * @exception IOException if an I/O error occurs. 317 */ 318 public void write(byte b[]) throws IOException { 319 Object traceContext = IoTrace.fileWriteBegin(path); 320 int bytesWritten = 0; 321 try { 322 writeBytes(b, 0, b.length, append); 323 bytesWritten = b.length; 324 } finally { 325 IoTrace.fileWriteEnd(traceContext, bytesWritten); 326 } 327 } 328 329 /** 330 * Writes <code>len</code> bytes from the specified byte array 331 * starting at offset <code>off</code> to this file output stream. 332 * 333 * @param b the data. 334 * @param off the start offset in the data. 335 * @param len the number of bytes to write. 336 * @exception IOException if an I/O error occurs. 337 */ 338 public void write(byte b[], int off, int len) throws IOException { 339 Object traceContext = IoTrace.fileWriteBegin(path); 340 int bytesWritten = 0; 341 try { 342 writeBytes(b, off, len, append); 343 bytesWritten = len; 344 } finally { 345 IoTrace.fileWriteEnd(traceContext, bytesWritten); 346 } 347 } 348 349 /** 350 * Closes this file output stream and releases any system resources 351 * associated with this stream. This file output stream may no longer 352 * be used for writing bytes. 353 * 354 * <p> If this stream has an associated channel then the channel is closed 355 * as well. 356 * 357 * @exception IOException if an I/O error occurs. 358 * 359 * @revised 1.4 360 * @spec JSR-51 361 */ 362 public void close() throws IOException { 363 synchronized (closeLock) { 364 if (closed) { 365 return; 366 } 367 closed = true; 368 } 369 370 if (channel != null) { 371 /* 372 * Decrement FD use count associated with the channel 373 * The use count is incremented whenever a new channel 374 * is obtained from this stream. 375 */ 376 fd.decrementAndGetUseCount(); 377 channel.close(); 378 } 379 380 /* 381 * Decrement FD use count associated with this stream 382 */ 383 int useCount = fd.decrementAndGetUseCount(); 384 385 /* 386 * If FileDescriptor is still in use by another stream, the finalizer 387 * will not close it. 388 */ 389 if ((useCount <= 0) || !isRunningFinalize()) { 390 close0(); 391 } 392 } 393 394 /** 395 * Returns the file descriptor associated with this stream. 396 * 397 * @return the <code>FileDescriptor</code> object that represents 398 * the connection to the file in the file system being used 399 * by this <code>FileOutputStream</code> object. 400 * 401 * @exception IOException if an I/O error occurs. 402 * @see java.io.FileDescriptor 403 */ 404 public final FileDescriptor getFD() throws IOException { 405 if (fd != null) return fd; 406 throw new IOException(); 407 } 408 409 /** 410 * Returns the unique {@link java.nio.channels.FileChannel FileChannel} 411 * object associated with this file output stream. </p> 412 * 413 * <p> The initial {@link java.nio.channels.FileChannel#position() 414 * </code>position<code>} of the returned channel will be equal to the 415 * number of bytes written to the file so far unless this stream is in 416 * append mode, in which case it will be equal to the size of the file. 417 * Writing bytes to this stream will increment the channel's position 418 * accordingly. Changing the channel's position, either explicitly or by 419 * writing, will change this stream's file position. 420 * 421 * @return the file channel associated with this file output stream 422 * 423 * @since 1.4 424 * @spec JSR-51 425 */ 426 public FileChannel getChannel() { 427 synchronized (this) { 428 if (channel == null) { 429 channel = FileChannelImpl.open(fd, path, false, true, append, this); 430 431 /* 432 * Increment fd's use count. Invoking the channel's close() 433 * method will result in decrementing the use count set for 434 * the channel. 435 */ 436 fd.incrementAndGetUseCount(); 437 } 438 return channel; 439 } 440 } 441 442 /** 443 * Cleans up the connection to the file, and ensures that the 444 * <code>close</code> method of this file output stream is 445 * called when there are no more references to this stream. 446 * 447 * @exception IOException if an I/O error occurs. 448 * @see java.io.FileInputStream#close() 449 */ 450 protected void finalize() throws IOException { 451 if (fd != null) { 452 if (fd == FileDescriptor.out || fd == FileDescriptor.err) { 453 flush(); 454 } else { 455 456 /* 457 * Finalizer should not release the FileDescriptor if another 458 * stream is still using it. If the user directly invokes 459 * close() then the FileDescriptor is also released. 460 */ 461 runningFinalize.set(Boolean.TRUE); 462 try { 463 close(); 464 } finally { 465 runningFinalize.set(Boolean.FALSE); 466 } 467 } 468 } 469 } 470 471 private native void close0() throws IOException; 472 473 private static native void initIDs(); 474 475 static { 476 initIDs(); 477 } 478 479 }