001 /*
002 * Copyright 1994-2007 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025
026 package java.io;
027
028 import java.nio.channels.FileChannel;
029 import sun.nio.ch.FileChannelImpl;
030
031 /**
032 * A file output stream is an output stream for writing data to a
033 * <code>File</code> or to a <code>FileDescriptor</code>. Whether or not
034 * a file is available or may be created depends upon the underlying
035 * platform. Some platforms, in particular, allow a file to be opened
036 * for writing by only one <tt>FileOutputStream</tt> (or other
037 * file-writing object) at a time. In such situations the constructors in
038 * this class will fail if the file involved is already open.
039 *
040 * <p><code>FileOutputStream</code> is meant for writing streams of raw bytes
041 * such as image data. For writing streams of characters, consider using
042 * <code>FileWriter</code>.
043 *
044 * @author Arthur van Hoff
045 * @version 1.68, 06/13/07
046 * @see java.io.File
047 * @see java.io.FileDescriptor
048 * @see java.io.FileInputStream
049 * @since JDK1.0
050 */
051 public class FileOutputStream extends OutputStream {
052 /**
053 * The system dependent file descriptor. The value is
054 * 1 more than actual file descriptor. This means that
055 * the default value 0 indicates that the file is not open.
056 */
057 private FileDescriptor fd;
058
059 private FileChannel channel = null;
060
061 private boolean append = false;
062
063 private Object closeLock = new Object();
064 private volatile boolean closed = false;
065 private static ThreadLocal<Boolean> runningFinalize = new ThreadLocal<Boolean>();
066
067 private static boolean isRunningFinalize() {
068 Boolean val;
069 if ((val = runningFinalize.get()) != null)
070 return val.booleanValue();
071 return false;
072 }
073
074 /**
075 * Creates an output file stream to write to the file with the
076 * specified name. A new <code>FileDescriptor</code> object is
077 * created to represent this file connection.
078 * <p>
079 * First, if there is a security manager, its <code>checkWrite</code>
080 * method is called with <code>name</code> as its argument.
081 * <p>
082 * If the file exists but is a directory rather than a regular file, does
083 * not exist but cannot be created, or cannot be opened for any other
084 * reason then a <code>FileNotFoundException</code> is thrown.
085 *
086 * @param name the system-dependent filename
087 * @exception FileNotFoundException if the file exists but is a directory
088 * rather than a regular file, does not exist but cannot
089 * be created, or cannot be opened for any other reason
090 * @exception SecurityException if a security manager exists and its
091 * <code>checkWrite</code> method denies write access
092 * to the file.
093 * @see java.lang.SecurityManager#checkWrite(java.lang.String)
094 */
095 public FileOutputStream(String name) throws FileNotFoundException {
096 this (name != null ? new File(name) : null, false);
097 }
098
099 /**
100 * Creates an output file stream to write to the file with the specified
101 * <code>name</code>. If the second argument is <code>true</code>, then
102 * bytes will be written to the end of the file rather than the beginning.
103 * A new <code>FileDescriptor</code> object is created to represent this
104 * file connection.
105 * <p>
106 * First, if there is a security manager, its <code>checkWrite</code>
107 * method is called with <code>name</code> as its argument.
108 * <p>
109 * If the file exists but is a directory rather than a regular file, does
110 * not exist but cannot be created, or cannot be opened for any other
111 * reason then a <code>FileNotFoundException</code> is thrown.
112 *
113 * @param name the system-dependent file name
114 * @param append if <code>true</code>, then bytes will be written
115 * to the end of the file rather than the beginning
116 * @exception FileNotFoundException if the file exists but is a directory
117 * rather than a regular file, does not exist but cannot
118 * be created, or cannot be opened for any other reason.
119 * @exception SecurityException if a security manager exists and its
120 * <code>checkWrite</code> method denies write access
121 * to the file.
122 * @see java.lang.SecurityManager#checkWrite(java.lang.String)
123 * @since JDK1.1
124 */
125 public FileOutputStream(String name, boolean append)
126 throws FileNotFoundException {
127 this (name != null ? new File(name) : null, append);
128 }
129
130 /**
131 * Creates a file output stream to write to the file represented by
132 * the specified <code>File</code> object. A new
133 * <code>FileDescriptor</code> object is created to represent this
134 * file connection.
135 * <p>
136 * First, if there is a security manager, its <code>checkWrite</code>
137 * method is called with the path represented by the <code>file</code>
138 * argument as its argument.
139 * <p>
140 * If the file exists but is a directory rather than a regular file, does
141 * not exist but cannot be created, or cannot be opened for any other
142 * reason then a <code>FileNotFoundException</code> is thrown.
143 *
144 * @param file the file to be opened for writing.
145 * @exception FileNotFoundException if the file exists but is a directory
146 * rather than a regular file, does not exist but cannot
147 * be created, or cannot be opened for any other reason
148 * @exception SecurityException if a security manager exists and its
149 * <code>checkWrite</code> method denies write access
150 * to the file.
151 * @see java.io.File#getPath()
152 * @see java.lang.SecurityException
153 * @see java.lang.SecurityManager#checkWrite(java.lang.String)
154 */
155 public FileOutputStream(File file) throws FileNotFoundException {
156 this (file, false);
157 }
158
159 /**
160 * Creates a file output stream to write to the file represented by
161 * the specified <code>File</code> object. If the second argument is
162 * <code>true</code>, then bytes will be written to the end of the file
163 * rather than the beginning. A new <code>FileDescriptor</code> object is
164 * created to represent this file connection.
165 * <p>
166 * First, if there is a security manager, its <code>checkWrite</code>
167 * method is called with the path represented by the <code>file</code>
168 * argument as its argument.
169 * <p>
170 * If the file exists but is a directory rather than a regular file, does
171 * not exist but cannot be created, or cannot be opened for any other
172 * reason then a <code>FileNotFoundException</code> is thrown.
173 *
174 * @param file the file to be opened for writing.
175 * @param append if <code>true</code>, then bytes will be written
176 * to the end of the file rather than the beginning
177 * @exception FileNotFoundException if the file exists but is a directory
178 * rather than a regular file, does not exist but cannot
179 * be created, or cannot be opened for any other reason
180 * @exception SecurityException if a security manager exists and its
181 * <code>checkWrite</code> method denies write access
182 * to the file.
183 * @see java.io.File#getPath()
184 * @see java.lang.SecurityException
185 * @see java.lang.SecurityManager#checkWrite(java.lang.String)
186 * @since 1.4
187 */
188 public FileOutputStream(File file, boolean append)
189 throws FileNotFoundException {
190 String name = (file != null ? file.getPath() : null);
191 SecurityManager security = System.getSecurityManager();
192 if (security != null) {
193 security.checkWrite(name);
194 }
195 if (name == null) {
196 throw new NullPointerException();
197 }
198 fd = new FileDescriptor();
199 fd.incrementAndGetUseCount();
200 this .append = append;
201 if (append) {
202 openAppend(name);
203 } else {
204 open(name);
205 }
206 }
207
208 /**
209 * Creates an output file stream to write to the specified file
210 * descriptor, which represents an existing connection to an actual
211 * file in the file system.
212 * <p>
213 * First, if there is a security manager, its <code>checkWrite</code>
214 * method is called with the file descriptor <code>fdObj</code>
215 * argument as its argument.
216 * <p>
217 * If <code>fdObj</code> is null then a <code>NullPointerException</code>
218 * is thrown.
219 * <p>
220 * This constructor does not throw an exception if <code>fdObj</code>
221 * is {link java.io.FileDescriptor#valid() invalid}.
222 * However, if the methods are invoked on the resulting stream to attempt
223 * I/O on the stream, an <code>IOException</code> is thrown.
224 *
225 * @param fdObj the file descriptor to be opened for writing
226 * @exception SecurityException if a security manager exists and its
227 * <code>checkWrite</code> method denies
228 * write access to the file descriptor
229 * @see java.lang.SecurityManager#checkWrite(java.io.FileDescriptor)
230 */
231 public FileOutputStream(FileDescriptor fdObj) {
232 SecurityManager security = System.getSecurityManager();
233 if (fdObj == null) {
234 throw new NullPointerException();
235 }
236 if (security != null) {
237 security.checkWrite(fdObj);
238 }
239 fd = fdObj;
240
241 /*
242 * FileDescriptor is being shared by streams.
243 * Ensure that it's GC'ed only when all the streams/channels are done
244 * using it.
245 */
246 fd.incrementAndGetUseCount();
247 }
248
249 /**
250 * Opens a file, with the specified name, for writing.
251 * @param name name of file to be opened
252 */
253 private native void open(String name) throws FileNotFoundException;
254
255 /**
256 * Opens a file, with the specified name, for appending.
257 * @param name name of file to be opened
258 */
259 private native void openAppend(String name)
260 throws FileNotFoundException;
261
262 /**
263 * Writes the specified byte to this file output stream. Implements
264 * the <code>write</code> method of <code>OutputStream</code>.
265 *
266 * @param b the byte to be written.
267 * @exception IOException if an I/O error occurs.
268 */
269 public native void write(int b) throws IOException;
270
271 /**
272 * Writes a sub array as a sequence of bytes.
273 * @param b the data to be written
274 * @param off the start offset in the data
275 * @param len the number of bytes that are written
276 * @exception IOException If an I/O error has occurred.
277 */
278 private native void writeBytes(byte b[], int off, int len)
279 throws IOException;
280
281 /**
282 * Writes <code>b.length</code> bytes from the specified byte array
283 * to this file output stream.
284 *
285 * @param b the data.
286 * @exception IOException if an I/O error occurs.
287 */
288 public void write(byte b[]) throws IOException {
289 writeBytes(b, 0, b.length);
290 }
291
292 /**
293 * Writes <code>len</code> bytes from the specified byte array
294 * starting at offset <code>off</code> to this file output stream.
295 *
296 * @param b the data.
297 * @param off the start offset in the data.
298 * @param len the number of bytes to write.
299 * @exception IOException if an I/O error occurs.
300 */
301 public void write(byte b[], int off, int len) throws IOException {
302 writeBytes(b, off, len);
303 }
304
305 /**
306 * Closes this file output stream and releases any system resources
307 * associated with this stream. This file output stream may no longer
308 * be used for writing bytes.
309 *
310 * <p> If this stream has an associated channel then the channel is closed
311 * as well.
312 *
313 * @exception IOException if an I/O error occurs.
314 *
315 * @revised 1.4
316 * @spec JSR-51
317 */
318 public void close() throws IOException {
319 synchronized (closeLock) {
320 if (closed) {
321 return;
322 }
323 closed = true;
324 }
325
326 if (channel != null) {
327 /*
328 * Decrement FD use count associated with the channel
329 * The use count is incremented whenever a new channel
330 * is obtained from this stream.
331 */
332 fd.decrementAndGetUseCount();
333 channel.close();
334 }
335
336 /*
337 * Decrement FD use count associated with this stream
338 */
339 int useCount = fd.decrementAndGetUseCount();
340
341 /*
342 * If FileDescriptor is still in use by another stream, the finalizer
343 * will not close it.
344 */
345 if ((useCount <= 0) || !isRunningFinalize()) {
346 close0();
347 }
348 }
349
350 /**
351 * Returns the file descriptor associated with this stream.
352 *
353 * @return the <code>FileDescriptor</code> object that represents
354 * the connection to the file in the file system being used
355 * by this <code>FileOutputStream</code> object.
356 *
357 * @exception IOException if an I/O error occurs.
358 * @see java.io.FileDescriptor
359 */
360 public final FileDescriptor getFD() throws IOException {
361 if (fd != null)
362 return fd;
363 throw new IOException();
364 }
365
366 /**
367 * Returns the unique {@link java.nio.channels.FileChannel FileChannel}
368 * object associated with this file output stream. </p>
369 *
370 * <p> The initial {@link java.nio.channels.FileChannel#position()
371 * </code>position<code>} of the returned channel will be equal to the
372 * number of bytes written to the file so far unless this stream is in
373 * append mode, in which case it will be equal to the size of the file.
374 * Writing bytes to this stream will increment the channel's position
375 * accordingly. Changing the channel's position, either explicitly or by
376 * writing, will change this stream's file position.
377 *
378 * @return the file channel associated with this file output stream
379 *
380 * @since 1.4
381 * @spec JSR-51
382 */
383 public FileChannel getChannel() {
384 synchronized (this ) {
385 if (channel == null) {
386 channel = FileChannelImpl.open(fd, false, true, this ,
387 append);
388
389 /*
390 * Increment fd's use count. Invoking the channel's close()
391 * method will result in decrementing the use count set for
392 * the channel.
393 */
394 fd.incrementAndGetUseCount();
395 }
396 return channel;
397 }
398 }
399
400 /**
401 * Cleans up the connection to the file, and ensures that the
402 * <code>close</code> method of this file output stream is
403 * called when there are no more references to this stream.
404 *
405 * @exception IOException if an I/O error occurs.
406 * @see java.io.FileInputStream#close()
407 */
408 protected void finalize() throws IOException {
409 if (fd != null) {
410 if (fd == fd.out || fd == fd.err) {
411 flush();
412 } else {
413
414 /*
415 * Finalizer should not release the FileDescriptor if another
416 * stream is still using it. If the user directly invokes
417 * close() then the FileDescriptor is also released.
418 */
419 runningFinalize.set(Boolean.TRUE);
420 try {
421 close();
422 } finally {
423 runningFinalize.set(Boolean.FALSE);
424 }
425 }
426 }
427 }
428
429 private native void close0() throws IOException;
430
431 private static native void initIDs();
432
433 static {
434 initIDs();
435 }
436
437 }
|