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