001// *************************************************************************************************************************** 002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * 003// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * 004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * 005// * with the License. You may obtain a copy of the License at * 006// * * 007// * http://www.apache.org/licenses/LICENSE-2.0 * 008// * * 009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * 010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * 011// * specific language governing permissions and limitations under the License. * 012// *************************************************************************************************************************** 013package org.apache.juneau.internal; 014 015import static org.apache.juneau.internal.ThrowableUtils.*; 016import static org.apache.juneau.internal.StringUtils.*; 017 018import java.io.*; 019import java.nio.charset.*; 020 021import org.apache.juneau.*; 022import org.apache.juneau.utils.*; 023 024/** 025 * Various I/O related utility methods. 026 */ 027public final class IOUtils { 028 029 /** UTF-8 charset */ 030 public static final Charset UTF8 = Charset.forName("UTF-8"); 031 032 /** 033 * Reads the contents of a file into a string. 034 * 035 * @param path The path of the file to read using default character encoding. 036 * @return The contents of the reader as a string, or <jk>null</jk> if file does not exist. 037 * @throws IOException If a problem occurred trying to read from the reader. 038 */ 039 public static String readFile(String path) throws IOException { 040 return read(new File(path)); 041 } 042 043 /** 044 * Reads the contents of a file into a string. 045 * 046 * @param in The file to read using default character encoding. 047 * @return The contents of the reader as a string, or <jk>null</jk> if file does not exist. 048 * @throws IOException If a problem occurred trying to read from the reader. 049 */ 050 public static String read(File in) throws IOException { 051 if (in == null || ! in.exists()) 052 return null; 053 try (Reader r = FileReaderBuilder.create(in).build()) { 054 return read(r, 0, 1024); 055 } 056 } 057 058 /** 059 * Reads the specified object to a <code>String</code>. 060 * 061 * <p> 062 * Can be any of the following object types: 063 * <ul> 064 * <li>{@link CharSequence} 065 * <li>{@link File} 066 * <li>{@link Reader} 067 * <li>{@link InputStream} 068 * <li><code><jk>byte</jk>[]</code> 069 * </ul> 070 * 071 * @param o The object to read. 072 * @return The object serialized to a string, or <jk>null</jk> if it wasn't a supported type. 073 * @throws IOException 074 */ 075 public static String read(Object o) throws IOException { 076 if (o instanceof CharSequence) 077 return o.toString(); 078 if (o instanceof File) 079 return read((File)o); 080 if (o instanceof Reader) 081 return read((Reader)o); 082 if (o instanceof InputStream) 083 return read((InputStream)o); 084 if (o instanceof byte[]) 085 return read(new ByteArrayInputStream((byte[])o)); 086 return null; 087 } 088 089 /** 090 * Writes the contents of the specified <code>Reader</code> to the specified file. 091 * 092 * @param out The file to write the output to. 093 * @param in The reader to pipe from. 094 * @return The number of characters written to the file. 095 * @throws IOException 096 */ 097 public static int write(File out, Reader in) throws IOException { 098 assertFieldNotNull(out, "out"); 099 assertFieldNotNull(in, "in"); 100 try (Writer w = FileWriterBuilder.create(out).build()) { 101 return IOPipe.create(in, w).run(); 102 } 103 } 104 105 /** 106 * Writes the contents of the specified <code>InputStream</code> to the specified file. 107 * 108 * @param out The file to write the output to. 109 * @param in The input stream to pipe from. 110 * @return The number of characters written to the file. 111 * @throws IOException 112 */ 113 public static int write(File out, InputStream in) throws IOException { 114 assertFieldNotNull(out, "out"); 115 assertFieldNotNull(in, "in"); 116 try (OutputStream os = new FileOutputStream(out)) { 117 return IOPipe.create(in, os).run(); 118 } 119 } 120 121 /** 122 * Reads the contents of a reader into a string. 123 * 124 * @param in The input reader. 125 * @return The contents of the reader as a string. 126 * @throws IOException If a problem occurred trying to read from the reader. 127 */ 128 public static String read(Reader in) throws IOException { 129 return read(in, 0, 1024); 130 } 131 132 /** 133 * Reads the contents of an input stream into a string using the specified charset. 134 * 135 * @param in The input stream. 136 * @param cs The charset of the contents of the input stream. 137 * @return The contents of the reader as a string. <jk>null</jk> if input stream was null. 138 * @throws IOException If a problem occurred trying to read from the input stream. 139 */ 140 public static String read(InputStream in, Charset cs) throws IOException { 141 if (in == null) 142 return null; 143 return read(new InputStreamReader(in, cs)); 144 } 145 146 /** 147 * Reads the contents of an input stream into a string using the system default charset. 148 * 149 * @param in The input stream. 150 * @return The contents of the reader as a string, or <jk>null</jk> if the input stream is null. 151 * @throws IOException If a problem occurred trying to read from the input stream. 152 */ 153 public static String read(InputStream in) throws IOException { 154 if (in == null) 155 return null; 156 return read(new InputStreamReader(in, Charset.defaultCharset())); 157 } 158 159 /** 160 * Read the specified input stream into a byte array and closes the stream. 161 * 162 * @param in The input stream. 163 * @param bufferSize The expected size of the buffer. 164 * @return The contents of the stream as a byte array. 165 * @throws IOException Thrown by underlying stream. 166 */ 167 public static byte[] readBytes(InputStream in, int bufferSize) throws IOException { 168 if (in == null) 169 return null; 170 ByteArrayOutputStream buff = new ByteArrayOutputStream(bufferSize); 171 int nRead; 172 byte[] b = new byte[Math.min(bufferSize, 8192)]; 173 174 try { 175 while ((nRead = in.read(b, 0, b.length)) != -1) 176 buff.write(b, 0, nRead); 177 buff.flush(); 178 179 return buff.toByteArray(); 180 } finally { 181 in.close(); 182 } 183 } 184 185 /** 186 * Reads a raw stream of bytes from the specified file. 187 * 188 * @param f The file to read. 189 * @return A byte array containing the contents of the file. 190 * @throws IOException 191 */ 192 public static byte[] readBytes(File f) throws IOException { 193 if (f == null || ! (f.exists() && f.canRead())) 194 return null; 195 196 try (FileInputStream fis = new FileInputStream(f)) { 197 return readBytes(fis, (int)f.length()); 198 } 199 } 200 201 /** 202 * Reads the specified input into a {@link String} until the end of the input is reached. 203 * 204 * <p> 205 * The {@code Reader} is automatically closed. 206 * 207 * <p> 208 * If the {@code Reader} is not an instance of a {@code BufferedReader}, then it gets wrapped in a 209 * {@code BufferedReader}. 210 * 211 * @param in The input reader. 212 * @param length Specify a positive number if the length of the input is known. 213 * @param bufferSize Specify the buffer size to use. 214 * @return The contents of the reader as a string. <jk>null</jk> if reader was null. 215 * @throws IOException If a problem occurred trying to read from the reader. 216 */ 217 public static String read(Reader in, int length, int bufferSize) throws IOException { 218 if (in == null) 219 return null; 220 length = (length <= 0 ? bufferSize : length); 221 StringBuilder sb = new StringBuilder(length); // Assume they're ASCII characters. 222 try { 223 char[] buf = new char[Math.min(bufferSize, length)]; 224 int i = 0; 225 while ((i = in.read(buf)) != -1) 226 sb.append(buf, 0, i); 227 return sb.toString(); 228 } finally { 229 in.close(); 230 } 231 } 232 233 /** 234 * Pipes the contents of the specified reader into the writer. 235 * 236 * <p> 237 * The reader is closed, the writer is not. 238 * 239 * @param in 240 * The reader to pipe from. 241 * @param out 242 * The writer to pipe to. 243 * @throws IOException 244 */ 245 public static void pipe(Reader in, Writer out) throws IOException { 246 assertFieldNotNull(out, "out"); 247 assertFieldNotNull(in, "in"); 248 IOPipe.create(in, out).run(); 249 } 250 251 /** 252 * Pipes the contents of the specified object into the writer. 253 * 254 * <p> 255 * The reader is closed, the writer is not. 256 * 257 * @param in 258 * The input to pipe from. 259 * Can be any of the types defined by {@link #toReader(Object)}. 260 * @param out 261 * The writer to pipe to. 262 * @throws IOException 263 */ 264 public static void pipe(Object in, Writer out) throws IOException { 265 pipe(toReader(in), out); 266 } 267 268 /** 269 * Pipes the contents of the specified streams. 270 * 271 * <p> 272 * The input stream is closed, the output stream is not. 273 * 274 * @param in 275 * The reader to pipe from. 276 * @param out 277 * The writer to pipe to. 278 * @throws IOException 279 */ 280 public static void pipe(InputStream in, OutputStream out) throws IOException { 281 assertFieldNotNull(out, "out"); 282 assertFieldNotNull(in, "in"); 283 IOPipe.create(in, out).run(); 284 } 285 286 /** 287 * Pipes the contents of the specified object into the output stream. 288 * 289 * <p> 290 * The input stream is closed, the output stream is not. 291 * 292 * @param in 293 * The input to pipe from. 294 * Can be any of the types defined by {@link #toInputStream(Object)}. 295 * @param out 296 * The writer to pipe to. 297 * @throws IOException 298 */ 299 public static void pipe(Object in, OutputStream out) throws IOException { 300 pipe(toInputStream(in), out); 301 } 302 303 /** 304 * Wraps the specified reader in a buffered reader. 305 * 306 * @param r The reader being wrapped. 307 * @return 308 * The reader wrapped in a {@link BufferedReader}, or the original {@link Reader} if it's already a buffered 309 * reader. 310 */ 311 public static Reader getBufferedReader(Reader r) { 312 if (r == null || r instanceof BufferedReader || r instanceof StringReader) 313 return r; 314 return new BufferedReader(r); 315 } 316 317 /** 318 * Counts the number of bytes in the input stream and then closes the stream. 319 * 320 * @param is The input stream to read from. 321 * @return The number of bytes read. 322 * @throws IOException 323 */ 324 public static long count(InputStream is) throws IOException { 325 assertFieldNotNull(is, "is"); 326 long c = 0; 327 long i; 328 try { 329 while ((i = is.skip(1024)) != 0) 330 c += i; 331 } finally { 332 is.close(); 333 } 334 return c; 335 } 336 337 /** 338 * Counts the number of characters in the reader and then closes the reader. 339 * 340 * @param r The reader to read from. 341 * @return The number of characters read. 342 * @throws IOException 343 */ 344 public static long count(Reader r) throws IOException { 345 assertFieldNotNull(r, "r"); 346 long c = 0; 347 long i; 348 try { 349 while ((i = r.skip(1024)) != 0) 350 c += i; 351 } finally { 352 r.close(); 353 } 354 return c; 355 } 356 357 /** 358 * Given the specified <js>"Content-Length"</js> header value, return an appropriate buffer size. 359 * 360 * <p> 361 * The maximum buffer size is 1MB. 362 * 363 * @param contentLength The value of the <js>"Content-Length"</js> header. 364 * @return The appropriate buffer size. 365 */ 366 public static int getBufferSize(String contentLength) { 367 try { 368 if (isNotEmpty(contentLength)) { 369 long l = Long.decode(contentLength); 370 if (l > 1048576) 371 return 1048576; 372 if (l <= 0) 373 return 8192; 374 return (int)l; 375 } 376 } catch (Exception e) { 377 return 8192; 378 } 379 return 8192; 380 } 381 382 /** 383 * Close input stream and ignore any exceptions. 384 * 385 * <p> 386 * No-op if input stream is <jk>null</jk>. 387 * 388 * @param is The input stream to close. 389 */ 390 public static void closeQuietly(InputStream is) { 391 try { 392 if (is != null) 393 is.close(); 394 } catch (IOException e) {} 395 } 396 397 /** 398 * Close output stream and ignore any exceptions. 399 * 400 * <p> 401 * No-op if output stream is <jk>null</jk>. 402 * 403 * @param os The output stream to close. 404 */ 405 public static void closeQuietly(OutputStream os) { 406 try { 407 if (os != null) 408 os.close(); 409 } catch (IOException e) {} 410 } 411 412 /** 413 * Close reader and ignore any exceptions. 414 * 415 * <p> 416 * No-op if reader is <jk>null</jk>. 417 * 418 * @param r The reader to close. 419 */ 420 public static void closeQuietly(Reader r) { 421 try { 422 if (r != null) 423 r.close(); 424 } catch (IOException e) {} 425 } 426 427 /** 428 * Close writer and ignore any exceptions. 429 * 430 * <p> 431 * No-op if writer is <jk>null</jk>. 432 * 433 * @param w The writer to close. 434 */ 435 public static void closeQuietly(Writer w) { 436 try { 437 if (w != null) 438 w.close(); 439 } catch (IOException e) {} 440 } 441 442 /** 443 * Quietly close all specified input streams, output streams, readers, and writers. 444 * 445 * @param o The list of all objects to quietly close. 446 */ 447 public static void closeQuietly(Object...o) { 448 for (Object o2 : o) { 449 if (o2 instanceof InputStream) 450 closeQuietly((InputStream)o2); 451 if (o2 instanceof OutputStream) 452 closeQuietly((OutputStream)o2); 453 if (o2 instanceof Reader) 454 closeQuietly((Reader)o2); 455 if (o2 instanceof Writer) 456 closeQuietly((Writer)o2); 457 } 458 } 459 460 /** 461 * Flushes multiple output streams and writers in a single call. 462 * 463 * @param o 464 * The objects to flush. 465 * <jk>null</jk> entries are ignored. 466 * @throws IOException 467 */ 468 public static void flush(Object...o) throws IOException { 469 IOException ex = null; 470 for (Object o2 : o) { 471 try { 472 if (o2 instanceof OutputStream) 473 ((OutputStream)o2).flush(); 474 if (o2 instanceof Writer) 475 ((Writer)o2).flush(); 476 } catch (IOException e) { 477 ex = e; 478 } 479 } 480 if (ex != null) 481 throw ex; 482 } 483 484 /** 485 * Close all specified input streams, output streams, readers, and writers. 486 * 487 * @param o 488 * The list of all objects to close. 489 * <jk>null</jk> entries are ignored. 490 * @throws IOException 491 */ 492 public static void close(Object...o) throws IOException { 493 IOException ex = null; 494 for (Object o2 : o) { 495 try { 496 if (o2 instanceof InputStream) 497 ((InputStream)o2).close(); 498 if (o2 instanceof OutputStream) 499 ((OutputStream)o2).close(); 500 if (o2 instanceof Reader) 501 ((Reader)o2).close(); 502 if (o2 instanceof Writer) 503 ((Writer)o2).close(); 504 } catch (IOException e) { 505 ex = e; 506 } 507 } 508 if (ex != null) 509 throw ex; 510 } 511 512 /** 513 * Converts an object to a <code>Reader</code>. 514 * 515 * @param o 516 * The object to convert to a reader. 517 * Can be any of the following: 518 * <ul> 519 * <li>{@link InputStream} 520 * <li>{@link Reader} 521 * <li>{@link File} 522 * <li>{@link CharSequence} 523 * <li><code><jk>byte</jk>[]</code> 524 * <li><code><jk>null</jk></code> - Returns <jk>null</jk>. 525 * </ul> 526 * @return The object converted to a reader. 527 * @throws IOException If file could not be read. 528 * @throws IllegalArgumentException If invalid object passed in. 529 */ 530 public static Reader toReader(Object o) throws IOException { 531 if (o == null) 532 return null; 533 if (o instanceof CharSequence) 534 return new StringReader(o.toString()); 535 if (o instanceof File) 536 return new FileReader((File)o); 537 if (o instanceof Reader) 538 return (Reader)o; 539 if (o instanceof InputStream) 540 return new InputStreamReader((InputStream)o, "UTF-8"); 541 if (o instanceof byte[]) 542 return new InputStreamReader(new ByteArrayInputStream((byte[])o), "UTF-8"); 543 throw new FormattedIllegalArgumentException("Invalid object of type {0} passed to IOUtils.toReader(Object)", o.getClass()); 544 } 545 546 /** 547 * Converts an object to an <code>InputStream</code>. 548 * 549 * @param o 550 * The object to convert to an input stream. 551 * Can be any of the following: 552 * <ul> 553 * <li>{@link InputStream} 554 * <li>{@link Reader} 555 * <li>{@link File} 556 * <li>{@link CharSequence} - Converted to UTF-8 stream. 557 * <li><code><jk>byte</jk>[]</code> 558 * <li><code><jk>null</jk></code> - Returns <jk>null</jk>. 559 * </ul> 560 * @return The object converted to an input stream. 561 * @throws IOException If file could not be read. 562 * @throws IllegalArgumentException If invalid object passed in. 563 */ 564 public static InputStream toInputStream(Object o) throws IOException { 565 if (o == null) 566 return null; 567 if (o instanceof InputStream) 568 return (InputStream)o; 569 if (o instanceof File) 570 return new FileInputStream((File)o); 571 if (o instanceof byte[]) 572 return new ByteArrayInputStream((byte[])o); 573 if (o instanceof CharSequence) 574 return new ByteArrayInputStream(((CharSequence)o).toString().getBytes(UTF8)); 575 if (o instanceof Reader) 576 return new ByteArrayInputStream(IOUtils.read((Reader)o).getBytes(UTF8)); 577 throw new FormattedIllegalArgumentException("Invalid object of type {0} passed to IOUtils.toInputStream(Object)", o.getClass()); 578 } 579 580 /** 581 * Writes the specified string to the specified file. 582 * 583 * @param path The file path. 584 * @param contents The new file contents. 585 * @throws IOException 586 */ 587 public static void write(String path, String contents) throws IOException { 588 write(new File(path), new StringReader(contents)); 589 } 590}