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