001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions Copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2009 Sun Microsystems Inc.
015 * Portions Copyright 2010–2011 ApexIdentity Inc.
016 * Portions Copyright 2011-2015 ForgeRock AS.
017 */
018
019package org.forgerock.http.io;
020
021import java.io.ByteArrayInputStream;
022import java.io.File;
023import java.io.FileNotFoundException;
024import java.io.IOException;
025import java.io.InputStream;
026import java.io.OutputStream;
027import java.io.Reader;
028import java.io.Writer;
029
030import org.forgerock.util.Factory;
031
032/**
033 * Utility class that can stream to and from streams.
034 */
035public final class IO {
036
037    /**
038     * 8 KiB.
039     */
040    public static final int DEFAULT_TMP_INIT_LENGTH = 8 * 1_024;
041
042    /**
043     * 64 KiB.
044     */
045    public static final int DEFAULT_TMP_MEMORY_LIMIT = 64 * 1_024;
046
047    /**
048     * 1 GiB.
049     */
050    public static final int DEFAULT_TMP_FILE_LIMIT = 1 * 1_024 * 1_024 * 1_024;
051
052    /** Size of buffer to use during streaming. */
053    private static final int BUF_SIZE = 8 * 1_024;
054
055    private static final InputStream NULL_INPUT_STREAM = new ByteArrayInputStream(new byte[0]);
056
057    private static final OutputStream NULL_OUTPUT_STREAM = new OutputStream() {
058
059        @Override
060        public void write(final int b) throws IOException {
061            // goes nowhere, does nothing
062        }
063    };
064
065    /**
066     * Creates a new branching input stream that wraps a byte array.
067     *
068     * @param bytes
069     *            byte array to wrap with the branching input stream.
070     * @return The branching input stream.
071     */
072    public static BranchingInputStream newBranchingInputStream(final byte[] bytes) {
073        return new ByteArrayBranchingStream(bytes);
074    }
075
076    /**
077     * Creates a new branching input stream to wrap another input stream. All
078     * divergence between branches is maintained in a temporary buffer.
079     * <p>
080     * If the stream being wrapped is a branching input stream, this constructor
081     * will simply branch off of that existing stream rather than wrapping it
082     * with another branching input stream.
083     * <p>
084     * <strong>Note:</strong> This stream and any branches it creates are not
085     * safe for use by multiple concurrent threads.
086     *
087     * @param in
088     *            the stream to be wrapped.
089     * @param bufferFactory
090     *            an object that can create new temporary buffers (e.g. @link
091     *            TemporaryStorage}).
092     * @return The branching input stream.
093     */
094    public static BranchingInputStream newBranchingInputStream(final InputStream in,
095            final Factory<Buffer> bufferFactory) {
096        return new BranchingStreamWrapper(in, bufferFactory);
097    }
098
099    /**
100     * Creates a new file buffer that uses a local file for data storage.
101     * <p>
102     * <strong>Note:</strong> The returned buffer is not synchronized. If
103     * multiple threads access a buffer concurrently, threads that append to the
104     * buffer should synchronize on the instance of this object.
105     *
106     * @param file
107     *            the file to use as storage for the buffer.
108     * @param limit
109     *            the buffer length limit, after which an
110     *            {@link OverflowException} will be thrown.
111     * @return The file buffer.
112     * @throws FileNotFoundException
113     *             if the file cannot be created or opened for writing.
114     * @throws SecurityException
115     *             if a security manager denies access to the specified file.
116     */
117    public static Buffer newFileBuffer(final File file, final int limit)
118            throws FileNotFoundException {
119        return new FileBuffer(file, limit);
120    }
121
122    /**
123     * Creates a new buffer that uses a byte array for data storage. The byte
124     * array starts at a prescribed initial length, and grows exponentially up
125     * to the prescribed limit.
126     * <p>
127     * <strong>Note:</strong> The returned buffer is not synchronized. If
128     * multiple threads access a buffer concurrently, threads that append to the
129     * buffer should synchronize on the instance of this object.
130     *
131     * @param initial
132     *            the initial size of the byte array to create.
133     * @param limit
134     *            the buffer length limit, after which an
135     *            {@link OverflowException} will be thrown.
136     * @return The memory buffer.
137     */
138    public static Buffer newMemoryBuffer(final int initial, final int limit) {
139        return new MemoryBuffer(initial, limit);
140    }
141
142    /**
143     * Creates a new temporary buffer that first uses memory, then a temporary
144     * file for data storage. Initially, a {@link #newMemoryBuffer(int, int)
145     * memory} buffer is used; when the memory buffer limit is exceeded it
146     * promotes to the use of a {@link #newFileBuffer(File, int) file} buffer.
147     *
148     * @param initialLength
149     *            the initial length of memory buffer byte array.
150     * @param memoryLimit
151     *            the length limit of the memory buffer.
152     * @param fileLimit
153     *            the length limit of the file buffer.
154     * @param directory
155     *            the directory where temporary files are created, or
156     *            {@code null} to use the system-dependent default temporary
157     *            directory.
158     * @return The temporary buffer.
159     */
160    public static Buffer newTemporaryBuffer(final int initialLength, final int memoryLimit,
161            final int fileLimit, final File directory) {
162        return new TemporaryBuffer(initialLength, memoryLimit, fileLimit, directory);
163    }
164
165    /**
166     * Creates a new storage using the system dependent default temporary
167     * directory and default sizes. Equivalent to call
168     * {@code newTemporaryStorage(null)}.
169     *
170     * @return The temporary storage.
171     */
172    public static Factory<Buffer> newTemporaryStorage() {
173        return newTemporaryStorage(null);
174    }
175
176    /**
177     * Builds a storage using the given directory (may be {@literal null}) and
178     * default sizes. Equivalent to call
179     * {@code newTemporaryStorage(directory, HEIGHT_KB, SIXTY_FOUR_KB, ONE_MB)}
180     * .
181     *
182     * @param directory
183     *            The directory where temporary files are created. If
184     *            {@code null}, then the system-dependent default temporary
185     *            directory will be used.
186     * @return The temporary storage.
187     */
188    public static Factory<Buffer> newTemporaryStorage(final File directory) {
189        return newTemporaryStorage(directory, DEFAULT_TMP_INIT_LENGTH, DEFAULT_TMP_MEMORY_LIMIT,
190                DEFAULT_TMP_FILE_LIMIT);
191    }
192
193    /**
194     * Builds a storage using the given directory (may be {@literal null}) and
195     * provided sizes.
196     *
197     * @param directory
198     *            The directory where temporary files are created. If
199     *            {@code null}, then the system-dependent default temporary
200     *            directory will be used.
201     * @param initialLength
202     *            The initial length of memory buffer byte array.
203     * @param memoryLimit
204     *            The length limit of the memory buffer. Attempts to exceed this
205     *            limit will result in promoting the buffer from a memory to a
206     *            file buffer.
207     * @param fileLimit
208     *            The length limit of the file buffer. Attempts to exceed this
209     *            limit will result in an {@link OverflowException} being
210     *            thrown.
211     * @return The temporary storage.
212     */
213    public static Factory<Buffer> newTemporaryStorage(final File directory,
214            final int initialLength, final int memoryLimit, final int fileLimit) {
215        return new Factory<Buffer>() {
216            @Override
217            public Buffer newInstance() {
218                return newTemporaryBuffer(initialLength, memoryLimit, fileLimit, directory);
219            }
220        };
221    }
222
223    /**
224     * Returns an input stream that holds no data.
225     *
226     * @return An input stream that holds no data.
227     */
228    public static InputStream nullInputStream() {
229        return NULL_INPUT_STREAM;
230    }
231
232    /**
233     * Returns an output stream that discards all data written to it.
234     *
235     * @return An output stream that discards all data written to it.
236     */
237    public static OutputStream nullOutputStream() {
238        return NULL_OUTPUT_STREAM;
239    }
240
241    /**
242     * Streams all data from an input stream to an output stream.
243     *
244     * @param in
245     *            the input stream to stream the data from.
246     * @param out
247     *            the output stream to stream the data to.
248     * @throws IOException
249     *             if an I/O exception occurs.
250     */
251    public static void stream(final InputStream in, final OutputStream out) throws IOException {
252        final byte[] buf = new byte[BUF_SIZE];
253        int n;
254        while ((n = in.read(buf, 0, BUF_SIZE)) != -1) {
255            out.write(buf, 0, n);
256        }
257    }
258
259    /**
260     * Streams data from an input stream to an output stream, up to a specified
261     * length.
262     *
263     * @param in
264     *            the input stream to stream the data from.
265     * @param out
266     *            the output stream to stream the data to.
267     * @param len
268     *            the number of bytes to stream.
269     * @return the actual number of bytes streamed.
270     * @throws IOException
271     *             if an I/O exception occurs.
272     */
273    public static int stream(final InputStream in, final OutputStream out, final int len)
274            throws IOException {
275        int remaining = len;
276        final byte[] buf = new byte[BUF_SIZE];
277        int n;
278        while (remaining > 0 && (n = in.read(buf, 0, Math.min(remaining, BUF_SIZE))) >= 0) {
279            out.write(buf, 0, n);
280            remaining -= n;
281        }
282        return len - remaining;
283    }
284
285    /**
286     * Streams all characters from a reader to a writer.
287     *
288     * @param in
289     *            reader to stream the characters from.
290     * @param out
291     *            the writer to stream the characters to.
292     * @throws IOException
293     *             if an I/O exception occurs.
294     */
295    public static void stream(final Reader in, final Writer out) throws IOException {
296        final char[] buf = new char[BUF_SIZE];
297        int n;
298        while ((n = in.read(buf, 0, BUF_SIZE)) != -1) {
299            out.write(buf, 0, n);
300        }
301    }
302
303    /** Static methods only. */
304    private IO() {
305    }
306}