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}