1/*
2 * Copyright (c) 1995, 2015, 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.util.ArrayList;
29import java.util.List;
30import jdk.internal.misc.JavaIOFileDescriptorAccess;
31import jdk.internal.misc.SharedSecrets;
32
33/**
34 * Instances of the file descriptor class serve as an opaque handle
35 * to the underlying machine-specific structure representing an open
36 * file, an open socket, or another source or sink of bytes. The
37 * main practical use for a file descriptor is to create a
38 * <code>FileInputStream</code> or <code>FileOutputStream</code> to
39 * contain it.
40 * <p>
41 * Applications should not create their own file descriptors.
42 *
43 * @author  Pavani Diwanji
44 * @see     java.io.FileInputStream
45 * @see     java.io.FileOutputStream
46 * @since   1.0
47 */
48public final class FileDescriptor {
49
50    private int fd;
51
52    private Closeable parent;
53    private List<Closeable> otherParents;
54    private boolean closed;
55
56    /**
57     * true, if file is opened for appending.
58     */
59    private boolean append;
60
61    /**
62     * Constructs an (invalid) FileDescriptor
63     * object.
64     */
65    public FileDescriptor() {
66        fd = -1;
67    }
68
69    private FileDescriptor(int fd) {
70        this.fd = fd;
71        this.append = getAppend(fd);
72    }
73
74    /**
75     * A handle to the standard input stream. Usually, this file
76     * descriptor is not used directly, but rather via the input stream
77     * known as <code>System.in</code>.
78     *
79     * @see     java.lang.System#in
80     */
81    public static final FileDescriptor in = new FileDescriptor(0);
82
83    /**
84     * A handle to the standard output stream. Usually, this file
85     * descriptor is not used directly, but rather via the output stream
86     * known as <code>System.out</code>.
87     * @see     java.lang.System#out
88     */
89    public static final FileDescriptor out = new FileDescriptor(1);
90
91    /**
92     * A handle to the standard error stream. Usually, this file
93     * descriptor is not used directly, but rather via the output stream
94     * known as <code>System.err</code>.
95     *
96     * @see     java.lang.System#err
97     */
98    public static final FileDescriptor err = new FileDescriptor(2);
99
100    /**
101     * Tests if this file descriptor object is valid.
102     *
103     * @return  <code>true</code> if the file descriptor object represents a
104     *          valid, open file, socket, or other active I/O connection;
105     *          <code>false</code> otherwise.
106     */
107    public boolean valid() {
108        return fd != -1;
109    }
110
111    /**
112     * Force all system buffers to synchronize with the underlying
113     * device.  This method returns after all modified data and
114     * attributes of this FileDescriptor have been written to the
115     * relevant device(s).  In particular, if this FileDescriptor
116     * refers to a physical storage medium, such as a file in a file
117     * system, sync will not return until all in-memory modified copies
118     * of buffers associated with this FileDescriptor have been
119     * written to the physical medium.
120     *
121     * sync is meant to be used by code that requires physical
122     * storage (such as a file) to be in a known state  For
123     * example, a class that provided a simple transaction facility
124     * might use sync to ensure that all changes to a file caused
125     * by a given transaction were recorded on a storage medium.
126     *
127     * sync only affects buffers downstream of this FileDescriptor.  If
128     * any in-memory buffering is being done by the application (for
129     * example, by a BufferedOutputStream object), those buffers must
130     * be flushed into the FileDescriptor (for example, by invoking
131     * OutputStream.flush) before that data will be affected by sync.
132     *
133     * @exception SyncFailedException
134     *        Thrown when the buffers cannot be flushed,
135     *        or because the system cannot guarantee that all the
136     *        buffers have been synchronized with physical media.
137     * @since     1.1
138     */
139    public native void sync() throws SyncFailedException;
140
141    /* This routine initializes JNI field offsets for the class */
142    private static native void initIDs();
143
144    static {
145        initIDs();
146    }
147
148    // Set up JavaIOFileDescriptorAccess in SharedSecrets
149    static {
150        SharedSecrets.setJavaIOFileDescriptorAccess(
151            new JavaIOFileDescriptorAccess() {
152                public void set(FileDescriptor obj, int fd) {
153                    obj.fd = fd;
154                }
155
156                public int get(FileDescriptor obj) {
157                    return obj.fd;
158                }
159
160                public void setAppend(FileDescriptor obj, boolean append) {
161                    obj.append = append;
162                }
163
164                public boolean getAppend(FileDescriptor obj) {
165                    return obj.append;
166                }
167
168                public void setHandle(FileDescriptor obj, long handle) {
169                    throw new UnsupportedOperationException();
170                }
171
172                public long getHandle(FileDescriptor obj) {
173                    throw new UnsupportedOperationException();
174                }
175            }
176        );
177    }
178
179    /**
180     * Returns true, if the file was opened for appending.
181     */
182    private static native boolean getAppend(int fd);
183
184    /*
185     * Package private methods to track referents.
186     * If multiple streams point to the same FileDescriptor, we cycle
187     * through the list of all referents and call close()
188     */
189
190    /**
191     * Attach a Closeable to this FD for tracking.
192     * parent reference is added to otherParents when
193     * needed to make closeAll simpler.
194     */
195    synchronized void attach(Closeable c) {
196        if (parent == null) {
197            // first caller gets to do this
198            parent = c;
199        } else if (otherParents == null) {
200            otherParents = new ArrayList<>();
201            otherParents.add(parent);
202            otherParents.add(c);
203        } else {
204            otherParents.add(c);
205        }
206    }
207
208    /**
209     * Cycle through all Closeables sharing this FD and call
210     * close() on each one.
211     *
212     * The caller closeable gets to call close0().
213     */
214    @SuppressWarnings("try")
215    synchronized void closeAll(Closeable releaser) throws IOException {
216        if (!closed) {
217            closed = true;
218            IOException ioe = null;
219            try (releaser) {
220                if (otherParents != null) {
221                    for (Closeable referent : otherParents) {
222                        try {
223                            referent.close();
224                        } catch(IOException x) {
225                            if (ioe == null) {
226                                ioe = x;
227                            } else {
228                                ioe.addSuppressed(x);
229                            }
230                        }
231                    }
232                }
233            } catch(IOException ex) {
234                /*
235                 * If releaser close() throws IOException
236                 * add other exceptions as suppressed.
237                 */
238                if (ioe != null)
239                    ex.addSuppressed(ioe);
240                ioe = ex;
241            } finally {
242                if (ioe != null)
243                    throw ioe;
244            }
245        }
246    }
247}
248