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