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 specified object to a <c>String</c>. 045 * 046 * <p> 047 * Can be any of the following object types: 048 * <ul> 049 * <li>{@link CharSequence} 050 * <li>{@link File} 051 * <li>{@link Reader} 052 * <li>{@link InputStream} 053 * <li><code><jk>byte</jk>[]</code> 054 * </ul> 055 * 056 * @param in The object to read. 057 * @return The object serialized to a string, or <jk>null</jk> if it wasn't a supported type. 058 * @throws IOException Thrown by underlying stream. 059 */ 060 public static String read(Object in) throws IOException { 061 if (in == null) 062 return null; 063 if (in instanceof CharSequence) 064 return in.toString(); 065 if (in instanceof File) 066 return read((File)in); 067 if (in instanceof Reader) 068 return read((Reader)in); 069 if (in instanceof InputStream) 070 return read((InputStream)in); 071 if (in instanceof byte[]) 072 return read(new ByteArrayInputStream((byte[])in)); 073 throw new IOException("Cannot convert object of type '"+in.getClass().getName()+"' to a String."); 074 } 075 076 /** 077 * Same as {@link #read(Object)} but appends all the input into a single String. 078 * 079 * @param in The objects to read. 080 * @return The objects serialized to a string, never <jk>null</jk>. 081 * @throws IOException Thrown by underlying stream. 082 */ 083 public static String readAll(Object...in) throws IOException { 084 if (in.length == 1) 085 return read(in[0]); 086 StringWriter sw = new StringWriter(); 087 for (Object o : in) 088 sw.write(emptyIfNull(read(o))); 089 return sw.toString(); 090 } 091 092 /** 093 * Reads the contents of a file into a string. 094 * 095 * @param in The file to read using default character encoding. 096 * @return The contents of the reader as a string, or <jk>null</jk> if file does not exist. 097 * @throws IOException If a problem occurred trying to read from the reader. 098 */ 099 public static String read(File in) throws IOException { 100 if (in == null || ! in.exists()) 101 return null; 102 try (Reader r = FileReaderBuilder.create(in).build()) { 103 return read(r, 0, 1024); 104 } 105 } 106 107 /** 108 * Reads the contents of a reader into a string. 109 * 110 * @param in The input reader. 111 * @return The contents of the reader as a string. 112 * @throws IOException If a problem occurred trying to read from the reader. 113 */ 114 public static String read(Reader in) throws IOException { 115 return read(in, 0, 1024); 116 } 117 118 /** 119 * Reads the contents of an input stream into a string using the specified charset. 120 * 121 * @param in The input stream. 122 * @param cs The charset of the contents of the input stream. 123 * @return The contents of the reader as a string. <jk>null</jk> if input stream was null. 124 * @throws IOException If a problem occurred trying to read from the input stream. 125 */ 126 public static String read(InputStream in, Charset cs) throws IOException { 127 if (in == null) 128 return null; 129 return read(new InputStreamReader(in, cs)); 130 } 131 132 /** 133 * Reads the contents of an input stream into a string using the system default charset. 134 * 135 * @param in The input stream. 136 * @return The contents of the reader as a string, or <jk>null</jk> if the input stream is null. 137 * @throws IOException If a problem occurred trying to read from the input stream. 138 */ 139 public static String read(InputStream in) throws IOException { 140 if (in == null) 141 return null; 142 return read(new InputStreamReader(in, Charset.defaultCharset())); 143 } 144 145 /** 146 * Reads the specified input into a {@link String} until the end of the input is reached. 147 * 148 * <p> 149 * The {@code Reader} is automatically closed. 150 * 151 * <p> 152 * If the {@code Reader} is not an instance of a {@code BufferedReader}, then it gets wrapped in a 153 * {@code BufferedReader}. 154 * 155 * @param in The input reader. 156 * @param length Specify a positive number if the length of the input is known. 157 * @param bufferSize Specify the buffer size to use. 158 * @return The contents of the reader as a string. <jk>null</jk> if reader was null. 159 * @throws IOException If a problem occurred trying to read from the reader. 160 */ 161 public static String read(Reader in, int length, int bufferSize) throws IOException { 162 if (in == null) 163 return null; 164 if (bufferSize == 0) 165 bufferSize = 1024; 166 length = (length <= 0 ? bufferSize : length); 167 StringBuilder sb = new StringBuilder(length); // Assume they're ASCII characters. 168 try { 169 char[] buf = new char[Math.min(bufferSize, length)]; 170 int i = 0; 171 while ((i = in.read(buf)) != -1) 172 sb.append(buf, 0, i); 173 return sb.toString(); 174 } finally { 175 in.close(); 176 } 177 } 178 179 /** 180 * Read the specified object into a byte array. 181 * 182 * @param in 183 * The object to read into a byte array. 184 * <br>Can be any of the following types: 185 * <ul> 186 * <li><code><jk>byte</jk>[]</code> 187 * <li>{@link InputStream} 188 * <li>{@link Reader} 189 * <li>{@link CharSequence} 190 * <li>{@link File} 191 * </ul> 192 * @param buffSize 193 * The buffer size to use. 194 * @return The contents of the stream as a byte array. 195 * @throws IOException Thrown by underlying stream or if object is not a supported type. 196 */ 197 public static byte[] readBytes(Object in, int buffSize) throws IOException { 198 if (in == null) 199 return new byte[0]; 200 if (in instanceof byte[]) 201 return (byte[])in; 202 if (in instanceof CharSequence) 203 return in.toString().getBytes(UTF8); 204 if (in instanceof InputStream) 205 return readBytes((InputStream)in, buffSize); 206 if (in instanceof Reader) 207 return read((Reader)in, 0, buffSize).getBytes(UTF8); 208 if (in instanceof File) 209 return readBytes((File)in, buffSize); 210 throw new IOException("Cannot convert object of type '"+in.getClass().getName()+"' to a byte array."); 211 } 212 213 /** 214 * Read the specified input stream into a byte array. 215 * 216 * @param in 217 * The stream to read into a byte array. 218 * @return The contents of the stream as a byte array. 219 * @throws IOException Thrown by underlying stream. 220 */ 221 public static byte[] readBytes(InputStream in) throws IOException { 222 return in == null ? null : readBytes(in, 1024); 223 } 224 225 /** 226 * Read the specified input stream into a byte array. 227 * 228 * @param in 229 * The stream to read into a byte array. 230 * @param buffSize 231 * The buffer size to use. 232 * @return The contents of the stream as a byte array. 233 * @throws IOException Thrown by underlying stream. 234 */ 235 public static byte[] readBytes(InputStream in, int buffSize) throws IOException { 236 if (buffSize == 0) 237 buffSize = 1024; 238 try (final ByteArrayOutputStream buff = new ByteArrayOutputStream(buffSize)) { 239 int nRead; 240 byte[] b = new byte[buffSize]; 241 while ((nRead = in.read(b, 0, b.length)) != -1) 242 buff.write(b, 0, nRead); 243 buff.flush(); 244 return buff.toByteArray(); 245 } 246 } 247 248 /** 249 * Read the specified file into a byte array. 250 * 251 * @param in 252 * The file to read into a byte array. 253 * @return The contents of the file as a byte array. 254 * @throws IOException Thrown by underlying stream. 255 */ 256 public static byte[] readBytes(File in) throws IOException { 257 return readBytes(in, 1024); 258 } 259 260 /** 261 * Read the specified file into a byte array. 262 * 263 * @param in 264 * The file to read into a byte array. 265 * @param buffSize 266 * The buffer size to use. 267 * @return The contents of the file as a byte array. 268 * @throws IOException Thrown by underlying stream. 269 */ 270 public static byte[] readBytes(File in, int buffSize) throws IOException { 271 if (buffSize == 0) 272 buffSize = 1024; 273 if (! (in.exists() && in.canRead())) 274 return new byte[0]; 275 buffSize = Math.min((int)in.length(), buffSize); 276 try (FileInputStream fis = new FileInputStream(in)) { 277 return readBytes(fis, buffSize); 278 } 279 } 280 281 /** 282 * Shortcut for calling <c>readBytes(in, 1024);</c> 283 * 284 * @param in 285 * The object to read into a byte array. 286 * <br>Can be any of the following types: 287 * <ul> 288 * <li><code><jk>byte</jk>[]</code> 289 * <li>{@link InputStream} 290 * <li>{@link Reader} 291 * <li>{@link CharSequence} 292 * <li>{@link File} 293 * </ul> 294 * @return The contents of the stream as a byte array. 295 * @throws IOException Thrown by underlying stream or if object is not a supported type. 296 */ 297 public static byte[] readBytes(Object in) throws IOException { 298 return readBytes(in, 1024); 299 } 300 301 /** 302 * Same as {@link #readBytes(Object)} but appends all the input into a single byte array. 303 * 304 * @param in The objects to read. 305 * @return The objects serialized to a byte array, never <jk>null</jk>. 306 * @throws IOException Thrown by underlying stream. 307 */ 308 public static byte[] readBytes(Object...in) throws IOException { 309 if (in.length == 1) 310 return readBytes(in[0]); 311 try (final ByteArrayOutputStream buff = new ByteArrayOutputStream(1024)) { 312 for (Object o : in) { 313 byte[] bo = readBytes(o); 314 if (bo != null) 315 buff.write(bo); 316 } 317 buff.flush(); 318 return buff.toByteArray(); 319 } 320 } 321 322 /** 323 * Writes the contents of the specified <c>Reader</c> to the specified file. 324 * 325 * @param out The file to write the output to. 326 * @param in The reader to pipe from. 327 * @return The number of characters written to the file. 328 * @throws IOException Thrown by underlying stream. 329 */ 330 public static int write(File out, Reader in) throws IOException { 331 assertFieldNotNull(out, "out"); 332 assertFieldNotNull(in, "in"); 333 try (Writer w = FileWriterBuilder.create(out).build()) { 334 return IOPipe.create(in, w).run(); 335 } 336 } 337 338 /** 339 * Writes the contents of the specified <c>InputStream</c> to the specified file. 340 * 341 * @param out The file to write the output to. 342 * @param in The input stream to pipe from. 343 * @return The number of characters written to the file. 344 * @throws IOException Thrown by underlying stream. 345 */ 346 public static int write(File out, InputStream in) throws IOException { 347 assertFieldNotNull(out, "out"); 348 assertFieldNotNull(in, "in"); 349 try (OutputStream os = new FileOutputStream(out)) { 350 return IOPipe.create(in, os).run(); 351 } 352 } 353 354 /** 355 * Pipes the contents of the specified object into the writer. 356 * 357 * <p> 358 * The reader is closed, the writer is not. 359 * 360 * @param in 361 * The input to pipe from. 362 * Can be any of the types defined by {@link #toReader(Object)}. 363 * @param out 364 * The writer to pipe to. 365 * @throws IOException Thrown by underlying stream. 366 */ 367 public static void pipe(Object in, Writer out) throws IOException { 368 IOPipe.create(in, out).run(); 369 } 370 371 /** 372 * Pipes the contents of the specified object into the output stream. 373 * 374 * <p> 375 * The input stream is closed, the output stream is not. 376 * 377 * @param in 378 * The input to pipe from. 379 * Can be any of the types defined by {@link #toInputStream(Object)}. 380 * @param out 381 * The writer to pipe to. 382 * @throws IOException Thrown by underlying stream. 383 */ 384 public static void pipe(Object in, OutputStream out) throws IOException { 385 IOPipe.create(in, out).run(); 386 } 387 388 /** 389 * Wraps the specified reader in a buffered reader. 390 * 391 * @param r The reader being wrapped. 392 * @return 393 * The reader wrapped in a {@link BufferedReader}, or the original {@link Reader} if it's already a buffered 394 * reader. 395 */ 396 public static Reader getBufferedReader(Reader r) { 397 if (r == null || r instanceof BufferedReader || r instanceof StringReader) 398 return r; 399 return new BufferedReader(r); 400 } 401 402 /** 403 * Counts the number of bytes in the input stream and then closes the stream. 404 * 405 * @param is The input stream to read from. 406 * @return The number of bytes read. 407 * @throws IOException Thrown by underlying stream. 408 */ 409 public static long count(InputStream is) throws IOException { 410 assertFieldNotNull(is, "is"); 411 long c = 0; 412 long i; 413 try { 414 while ((i = is.skip(1024)) != 0) 415 c += i; 416 } finally { 417 is.close(); 418 } 419 return c; 420 } 421 422 /** 423 * Counts the number of characters in the reader and then closes the reader. 424 * 425 * @param r The reader to read from. 426 * @return The number of characters read. 427 * @throws IOException Thrown by underlying stream. 428 */ 429 public static long count(Reader r) throws IOException { 430 assertFieldNotNull(r, "r"); 431 long c = 0; 432 long i; 433 try { 434 while ((i = r.skip(1024)) != 0) 435 c += i; 436 } finally { 437 r.close(); 438 } 439 return c; 440 } 441 442 /** 443 * Given the specified <js>"Content-Length"</js> header value, return an appropriate buffer size. 444 * 445 * <p> 446 * The maximum buffer size is 1MB. 447 * 448 * @param contentLength The value of the <js>"Content-Length"</js> header. 449 * @return The appropriate buffer size. 450 */ 451 public static int getBufferSize(String contentLength) { 452 try { 453 if (isNotEmpty(contentLength)) { 454 long l = Long.decode(contentLength); 455 if (l > 1048576) 456 return 1048576; 457 if (l <= 0) 458 return 8192; 459 return (int)l; 460 } 461 } catch (Exception e) { 462 return 8192; 463 } 464 return 8192; 465 } 466 467 /** 468 * Close input stream and ignore any exceptions. 469 * 470 * <p> 471 * No-op if input stream is <jk>null</jk>. 472 * 473 * @param is The input stream to close. 474 */ 475 public static void closeQuietly(InputStream is) { 476 try { 477 if (is != null) 478 is.close(); 479 } catch (IOException e) {} 480 } 481 482 /** 483 * Close output stream and ignore any exceptions. 484 * 485 * <p> 486 * No-op if output stream is <jk>null</jk>. 487 * 488 * @param os The output stream to close. 489 */ 490 public static void closeQuietly(OutputStream os) { 491 try { 492 if (os != null) 493 os.close(); 494 } catch (IOException e) {} 495 } 496 497 /** 498 * Close reader and ignore any exceptions. 499 * 500 * <p> 501 * No-op if reader is <jk>null</jk>. 502 * 503 * @param r The reader to close. 504 */ 505 public static void closeQuietly(Reader r) { 506 try { 507 if (r != null) 508 r.close(); 509 } catch (IOException e) {} 510 } 511 512 /** 513 * Close writer and ignore any exceptions. 514 * 515 * <p> 516 * No-op if writer is <jk>null</jk>. 517 * 518 * @param w The writer to close. 519 */ 520 public static void closeQuietly(Writer w) { 521 try { 522 if (w != null) 523 w.close(); 524 } catch (IOException e) {} 525 } 526 527 /** 528 * Quietly close all specified input streams, output streams, readers, and writers. 529 * 530 * @param o The list of all objects to quietly close. 531 */ 532 public static void closeQuietly(Object...o) { 533 for (Object o2 : o) { 534 if (o2 instanceof InputStream) 535 closeQuietly((InputStream)o2); 536 if (o2 instanceof OutputStream) 537 closeQuietly((OutputStream)o2); 538 if (o2 instanceof Reader) 539 closeQuietly((Reader)o2); 540 if (o2 instanceof Writer) 541 closeQuietly((Writer)o2); 542 } 543 } 544 545 /** 546 * Flushes multiple output streams and writers in a single call. 547 * 548 * @param o 549 * The objects to flush. 550 * <jk>null</jk> entries are ignored. 551 * @throws IOException Thrown by underlying stream. 552 */ 553 public static void flush(Object...o) throws IOException { 554 IOException ex = null; 555 for (Object o2 : o) { 556 try { 557 if (o2 instanceof OutputStream) 558 ((OutputStream)o2).flush(); 559 if (o2 instanceof Writer) 560 ((Writer)o2).flush(); 561 } catch (IOException e) { 562 ex = e; 563 } 564 } 565 if (ex != null) 566 throw ex; 567 } 568 569 /** 570 * Close all specified input streams, output streams, readers, and writers. 571 * 572 * @param o 573 * The list of all objects to close. 574 * <jk>null</jk> entries are ignored. 575 * @throws IOException Thrown by underlying stream. 576 */ 577 public static void close(Object...o) throws IOException { 578 IOException ex = null; 579 for (Object o2 : o) { 580 try { 581 if (o2 instanceof InputStream) 582 ((InputStream)o2).close(); 583 if (o2 instanceof OutputStream) 584 ((OutputStream)o2).close(); 585 if (o2 instanceof Reader) 586 ((Reader)o2).close(); 587 if (o2 instanceof Writer) 588 ((Writer)o2).close(); 589 } catch (IOException e) { 590 ex = e; 591 } 592 } 593 if (ex != null) 594 throw ex; 595 } 596 597 /** 598 * Converts an object to a <c>Reader</c>. 599 * 600 * @param o 601 * The object to convert to a reader. 602 * Can be any of the following: 603 * <ul> 604 * <li>{@link InputStream} 605 * <li>{@link Reader} 606 * <li>{@link File} 607 * <li>{@link CharSequence} 608 * <li><code><jk>byte</jk>[]</code> 609 * <li><code><jk>null</jk></code> - Returns <jk>null</jk>. 610 * </ul> 611 * @return The object converted to a reader. 612 * @throws IOException If file could not be read. 613 * @throws IllegalArgumentException If invalid object passed in. 614 */ 615 public static Reader toReader(Object o) throws IOException { 616 if (o == null) 617 return null; 618 if (o instanceof CharSequence) 619 return new StringReader(o.toString()); 620 if (o instanceof File) 621 return new FileReader((File)o); 622 if (o instanceof Reader) 623 return (Reader)o; 624 if (o instanceof InputStream) 625 return new InputStreamReader((InputStream)o, "UTF-8"); 626 if (o instanceof byte[]) 627 return new InputStreamReader(new ByteArrayInputStream((byte[])o), "UTF-8"); 628 throw new FormattedIllegalArgumentException("Invalid object of type {0} passed to IOUtils.toReader(Object)", o.getClass()); 629 } 630 631 /** 632 * Converts an object to an <c>InputStream</c>. 633 * 634 * @param o 635 * The object to convert to an input stream. 636 * Can be any of the following: 637 * <ul> 638 * <li>{@link InputStream} 639 * <li>{@link Reader} 640 * <li>{@link File} 641 * <li>{@link CharSequence} - Converted to UTF-8 stream. 642 * <li><code><jk>byte</jk>[]</code> 643 * <li><code><jk>null</jk></code> - Returns <jk>null</jk>. 644 * </ul> 645 * @return The object converted to an input stream. 646 * @throws IOException If file could not be read. 647 * @throws IllegalArgumentException If invalid object passed in. 648 */ 649 public static InputStream toInputStream(Object o) throws IOException { 650 if (o == null) 651 return null; 652 if (o instanceof InputStream) 653 return (InputStream)o; 654 if (o instanceof File) 655 return new FileInputStream((File)o); 656 if (o instanceof byte[]) 657 return new ByteArrayInputStream((byte[])o); 658 if (o instanceof CharSequence) 659 return new ByteArrayInputStream(((CharSequence)o).toString().getBytes(UTF8)); 660 if (o instanceof Reader) 661 return new ByteArrayInputStream(IOUtils.read((Reader)o).getBytes(UTF8)); 662 throw new FormattedIllegalArgumentException("Invalid object of type {0} passed to IOUtils.toInputStream(Object)", o.getClass()); 663 } 664 665 /** 666 * Writes the specified string to the specified file. 667 * 668 * @param path The file path. 669 * @param contents The new file contents. 670 * @throws IOException Thrown by underlying stream. 671 */ 672 public static void write(String path, String contents) throws IOException { 673 write(new File(path), new StringReader(contents)); 674 } 675 676 /** 677 * Loads a text file from either the file system or classpath. 678 * 679 * @param name The file name. 680 * @param paths The paths to search. 681 * @return The file contents, or <jk>null</jk> if not found. 682 * @throws IOException Thrown by underlying stream. 683 */ 684 public static String loadSystemResourceAsString(String name, String...paths) throws IOException { 685 for (String path : paths) { 686 File p = new File(path); 687 if (p.exists()) { 688 File f = new File(p, name); 689 if (f.exists() && f.canRead()) 690 return read(f); 691 } 692 } 693 ClassLoader cl = Thread.currentThread().getContextClassLoader(); 694 if (cl == null) 695 cl = ClassLoader.getSystemClassLoader(); 696 for (String path : paths) { 697 String n = ".".equals(path) ? name : path + '/' + name; 698 try (InputStream is = cl.getResourceAsStream(n)) { 699 if (is != null) 700 return read(is); 701 } 702 try (InputStream is = ClassLoader.getSystemResourceAsStream(n)) { 703 if (is != null) 704 return read(is); 705 } 706 } 707 return null; 708 } 709}