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