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