All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.juneau.common.internal.IOUtils Maven / Gradle / Ivy

// ***************************************************************************************************************************
// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
// * with the License.  You may obtain a copy of the License at                                                              *
// *                                                                                                                         *
// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
// *                                                                                                                         *
// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
// * specific language governing permissions and limitations under the License.                                              *
// ***************************************************************************************************************************
package org.apache.juneau.common.internal;

import java.io.*;
import java.nio.charset.*;
import java.util.*;
import java.util.concurrent.atomic.*;
import java.util.function.*;

/**
 * Various I/O related utility methods.
 */
public final class IOUtils {

	/** UTF-8 charset */
	public static final Charset UTF8 = Charset.forName("UTF-8");

	/** Reusable empty input stream. */
	public static final InputStream EMPTY_INPUT_STREAM = new InputStream() {
		@Override
		public int read() {
			return -1;  // end of stream
		}
	};

	private static final int BUFF_SIZE = 1024;
	private static final ThreadLocal BYTE_BUFFER_CACHE = (Boolean.getBoolean("juneau.disableIoBufferReuse") ? null : new ThreadLocal<>());
	private static final ThreadLocal CHAR_BUFFER_CACHE = (Boolean.getBoolean("juneau.disableIoBufferReuse") ? null : new ThreadLocal<>());

	static final AtomicInteger BYTE_BUFFER_CACHE_HITS = new AtomicInteger();
	static final AtomicInteger BYTE_BUFFER_CACHE_MISSES = new AtomicInteger();
	static final AtomicInteger CHAR_BUFFER_CACHE_HITS = new AtomicInteger();
	static final AtomicInteger CHAR_BUFFER_CACHE_MISSES = new AtomicInteger();

	static {
		SystemUtils.shutdownMessage(()->"Byte buffer cache:  hits="+BYTE_BUFFER_CACHE_HITS.get()+", misses=" + BYTE_BUFFER_CACHE_MISSES);
		SystemUtils.shutdownMessage(()->"Char buffer cache:  hits="+CHAR_BUFFER_CACHE_HITS.get()+", misses=" + CHAR_BUFFER_CACHE_MISSES);
	}

	/** Reusable empty reader. */
	public static final Reader EMPTY_READER = new Reader() {
		@Override
		public int read() {
			return -1;  // end of stream
		}
		@Override
		public int read(char[] cbuf, int off, int len) throws IOException {
			return -1;  // end of stream
		}
		@Override
		public void close() throws IOException {}
	};

	//-----------------------------------------------------------------------------------------------------------------
	// Piping utilities.
	//-----------------------------------------------------------------------------------------------------------------

	/**
	 * Pipes the contents of the specified Reader to the specified file.
	 *
	 * @param in
	 * 	The reader to pipe from.
	 * 	
Can be null. *
Reader is automatically closed. * @param out * The file to write the output to. *
Can be null. * @return * The number of characters piped. * @throws IOException Thrown by underlying stream. */ public static long pipe(Reader in, File out) throws IOException { if (out == null || in == null) return 0; try (Writer w = FileWriterBuilder.create(out).buffered().build()) { return pipe(in, w); } } /** * Pipes the contents of the specified Reader to the specified Writer. * * @param in * The reader to pipe from. *
Can be null. *
Reader is automatically closed. * @param out * The file to write the output to. *
Can be null. *
Writer is flushed but not automatically closed. * @return * The number of characters piped. * @throws IOException Thrown by underlying stream. */ public static long pipe(Reader in, Writer out) throws IOException { if (out == null || in == null) return 0; long total = 0; try (Reader in2 = in) { char[] buffer = charBuffer(-1); int readLen; while ((readLen = in.read(buffer)) != -1) { out.write(buffer, 0, readLen); total += readLen; } } out.flush(); return total; } /** * Pipes the contents of the specified Reader to the specified Writer. * * @param in * The reader to pipe from. *
Can be null. *
Reader is automatically closed. * @param out * The file to write the output to. *
Can be null. *
Writer is flushed but not automatically closed. * @param onException Consumer of any {@link IOException I/O exceptions}. * @return * The number of characters piped. */ public static long pipe(Reader in, Writer out, Consumer onException) { try { return pipe(in, out); } catch (IOException e) { onException.accept(e); return -1; } } /** * Pipes the contents of the specified Reader to the specified Writer a line at a time. * *

* Writer is flushed after every line. Typically useful when writing to consoles. * * @param in * The reader to pipe from. *
Can be null. *
Reader is automatically closed. * @param out * The file to write the output to. *
Can be null. *
Writer is flushed but not automatically closed. * @return * The number of characters piped. * @throws IOException Thrown by underlying stream. */ public static long pipeLines(Reader in, Writer out) throws IOException { if (in == null || out == null) return 0; long total = 0; try (Reader in2 = in) { try (Scanner s = new Scanner(in2)) { while (s.hasNextLine()) { String l = s.nextLine(); if (l != null) { out.write(l); out.write("\n"); out.flush(); total += l.length() + 1; } } } } return total; } /** * Pipes the contents of the specified input stream to the writer. * * @param in * The stream to pipe from. *
Can be null. *
Streams is automatically closed. * @param out * The writer to pipe to. *
Can be null. *
Stream is not automatically closed. * @return * The number of bytes written. * @throws IOException Thrown by underlying stream. */ public static long pipe(InputStream in, Writer out) throws IOException { if (in == null || out == null) return 0; return pipe(new InputStreamReader(in, UTF8), out); } /** * Pipes the contents of the specified input stream to the writer. * * @param in * The stream to pipe from. *
Can be null. *
Streams is automatically closed. * @param out * The writer to pipe to. *
Can be null. *
Stream is not automatically closed. * @param onException Consumer of any {@link IOException I/O exceptions}. * @return * The number of bytes written. */ public static long pipe(InputStream in, Writer out, Consumer onException) { try { if (in == null || out == null) return 0; return pipe(new InputStreamReader(in, UTF8), out); } catch (IOException e) { onException.accept(e); return -2; } } /** * Pipes the specified input stream to the specified output stream. * *

* Either stream is not automatically closed. * * @param in * The input stream. *
Can be null. *
Stream is automatically closed. * @param out * The output stream. *
Can be null. *
Stream is not automatically closed. * @return The number of bytes written. * @throws IOException If thrown from either stream. */ public static long pipe(InputStream in, OutputStream out) throws IOException { try (InputStream in2 = in) { return pipe(in, out, -1); } } /** * Pipes the specified input stream to the specified output stream. * *

* Either stream is not automatically closed. * * @param in * The input stream. *
Can be null. *
Stream is automatically closed. * @param out * The output stream. *
Can be null. *
Stream is not automatically closed. * @param onException Consumer of any {@link IOException I/O exceptions}. * @return The number of bytes written. */ public static long pipe(InputStream in, OutputStream out, Consumer onException) { try { try (InputStream in2 = in) { return pipe(in, out, -1); } } catch (IOException e) { onException.accept(e); return -1; } } /** * Pipes the specified input stream to the specified output stream. * *

* Either stream is not automatically closed. * * @param in * The input stream. *
Can be null. *
Stream is not automatically closed. * @param out * The output stream. *
Can be null. *
Stream is not automatically closed. * @param maxBytes * The maximum number of bytes or -1 to read the entire input stream. * @return The number of bytes written. * @throws IOException If thrown from either stream. */ public static long pipe(InputStream in, OutputStream out, long maxBytes) throws IOException { if (in == null || out == null) return 0; byte[] buffer = byteBuffer((int)maxBytes); int readLen; long total = 0; if (maxBytes < 0) { while ((readLen = in.read(buffer)) != -1) { out.write(buffer, 0, readLen); total += readLen; } } else { long remaining = maxBytes; while (remaining > 0) { readLen = in.read(buffer, 0, buffSize(remaining)); if (readLen == -1) break; out.write(buffer, 0, readLen); total += readLen; remaining -= readLen; } } out.flush(); return total; } /** * Pipes the specified reader to the specified output stream. * * @param in * The input reader. *
Can be null. *
Stream is automatically closed. * @param out * The output stream. *
Can be null. *
Stream is not automatically closed. * @return The number of bytes written. * @throws IOException If thrown from output stream. */ public static long pipe(Reader in, OutputStream out) throws IOException { if (in == null || out == null) return 0; long total = 0; try (Reader in2 = in) { OutputStreamWriter osw = new OutputStreamWriter(out, UTF8); int i; char[] b = charBuffer(-1); while ((i = in.read(b)) > 0) { total += i; osw.write(b, 0, i); } osw.flush(); } return total; } /** * Pipes the specified reader to the specified output stream. * * @param in * The input reader. *
Can be null. *
Stream is automatically closed. * @param out * The output stream. *
Can be null. *
Stream is not automatically closed. * @param onException Consumer of any {@link IOException I/O exceptions}. * @return The number of bytes written. */ public static long pipe(Reader in, OutputStream out, Consumer onException) { try { return pipe(in, out); } catch (IOException e) { onException.accept(e); return -1; } } /** * Pipes the specified byte array to the specified output stream. * * @param in * The input byte array. *
Can be null. * @param out * The output stream. *
Can be null. *
Stream is not automatically closed. * @param maxBytes * The maximum number of bytes or -1 to read the entire byte array. * @return The number of bytes written. * @throws IOException If thrown from output stream. */ public static long pipe(byte[] in, OutputStream out, int maxBytes) throws IOException { if (in == null || out == null) return 0; int length = (maxBytes < 0 || maxBytes > in.length ) ? in.length : maxBytes; out.write(in, 0, length); return length; } //----------------------------------------------------------------------------------------------------------------- // Reading utilities. //----------------------------------------------------------------------------------------------------------------- /** * Pipes the specified object to the specified output stream. * * @param in * The input byte array. *
Can be null or any of the following types: *

    *
  • {@link Reader} *
  • {@link InputStream} *
  • {@link File} *
  • byte array. *
* @return The input converted to a string. * @throws IOException If thrown from output stream. */ public static String read(Object in) throws IOException { if (in == null) return null; if (in instanceof Reader) return read((Reader)in); if (in instanceof InputStream) return read((InputStream)in); if (in instanceof File) return read((File)in); if (in instanceof byte[]) return read((byte[])in); throw new IllegalArgumentException("Invalid type passed to read: " + in.getClass().getName()); } /** * Reads the specified byte array containing UTF-8 into a string. * * @param in * The input. *
Can be null. * @return The new string, or null if the input was null. */ public static String read(byte[] in) { return read(in, UTF8); } /** * Reads the specified byte array into a string. * * @param in * The input. *
Can be null. * @param charset The character set to use for decoding. * @return The new string, or null if the input was null. */ public static String read(byte[] in, Charset charset) { if (in == null) return null; return new String(in, charset); } /** * Reads the contents of a file into a string. * *

* Assumes default character encoding. * * @param in * The file to read. *
Can be null. * @return * The contents of the reader as a string, or null if file does not exist. * @throws IOException If a problem occurred trying to read from the reader. */ public static String read(File in) throws IOException { if (in == null || ! in.exists()) return null; try (Reader r = FileReaderBuilder.create(in).build()) { return read(r, in.length()); } } /** * Reads the contents of a reader into a string. * * @param in * The input reader. *
Can be null. *
Stream is automatically closed. * @return * The contents of the reader as a string, or null if the reader was null. * @throws IOException If a problem occurred trying to read from the reader. */ public static String read(Reader in) throws IOException { try (Reader in2 = in) { return read(in, -1); } } /** * Reads the contents of a reader into a string. * * @param in * The input reader. *
Can be null. *
Stream is automatically closed. * @param onException Consumer of any {@link IOException I/O exceptions}. * @return * The contents of the reader as a string, or null if the reader was null. */ public static String read(Reader in, Consumer onException) { try (Reader in2 = in) { return read(in, -1); } catch (IOException e) { onException.accept(e); return null; } } /** * Reads the specified input into a {@link String} until the end of the input is reached. * * @param in * The input reader. *
Can be null. *
String is automatically closed. * @param expectedLength * Specify a positive number if the length of the input is known, or -1 if unknown. * @return * The contents of the reader as a string, or null if the reader was null. * @throws IOException If a problem occurred trying to read from the reader. */ public static String read(Reader in, long expectedLength) throws IOException { if (in == null) return null; try (Reader in2 = in) { StringBuilder sb = new StringBuilder(buffSize(expectedLength)); // Assume they're ASCII characters. char[] buf = charBuffer((int)expectedLength); int i = 0; while ((i = in2.read(buf)) != -1) sb.append(buf, 0, i); return sb.toString(); } } /** * Reads the contents of an input stream into a string. * *

* Assumes UTF-8 encoding. * * @param in * The input stream. *
Can be null. *
Stream is automatically closed. * @return * The contents of the reader as a string, or null if the input stream was null. * @throws IOException If a problem occurred trying to read from the input stream. */ public static String read(InputStream in) throws IOException { return read(in, UTF8); } /** * Reads the contents of an input stream into a string. * *

* Assumes UTF-8 encoding. * * @param in * The input stream. *
Can be null. *
Stream is automatically closed. * @param onException Consumer of any {@link IOException I/O exceptions}. * @return * The contents of the reader as a string, or null if the input stream was null. */ public static String read(InputStream in, Consumer onException) { return read(in, UTF8, onException); } /** * Reads the contents of an input stream into a string using the specified charset. * * @param in * The input stream. *
Can be null. *
Stream is automatically closed. * @param cs * The charset of the contents of the input stream. * @return * The contents of the reader as a string or null if input stream was null. * @throws IOException If a problem occurred trying to read from the input stream. */ public static String read(InputStream in, Charset cs) throws IOException { if (in == null) return null; try (InputStreamReader isr = new InputStreamReader(in, cs)) { return read(isr); } } /** * Reads the contents of an input stream into a string using the specified charset. * * @param in * The input stream. *
Can be null. *
Stream is automatically closed. * @param cs * The charset of the contents of the input stream. * @param onException Consumer of any {@link IOException I/O exceptions}. * @return * The contents of the reader as a string or null if input stream was null. */ public static String read(InputStream in, Charset cs, Consumer onException) { if (in == null) return null; try (InputStreamReader isr = new InputStreamReader(in, cs)) { return read(isr); } catch (IOException e) { onException.accept(e); return null; } } /** * Reads the specified input stream into the specified byte array. * * @param in * The input stream to read. *
Can be null. *
Stream is automatically closed. * @return A byte array containing the contents. Never null. * @throws IOException Thrown by underlying stream. */ public static byte[] readBytes(InputStream in) throws IOException { try (InputStream in2 = in) { return readBytes(in2, -1); } } /** * Reads the specified input stream into the specified byte array. * * @param in * The input stream to read. *
Can be null. *
Stream is not automatically closed. * @param maxBytes * The maximum number of bytes or -1 to read the entire stream. * @return A byte array containing the contents. Never null. * @throws IOException Thrown by underlying stream. */ public static byte[] readBytes(InputStream in, int maxBytes) throws IOException { if (in == null) return new byte[0]; ByteArrayOutputStream buff = new ByteArrayOutputStream(buffSize(maxBytes)); int nRead; byte[] b = byteBuffer(maxBytes); while ((nRead = in.read(b, 0, b.length)) != -1) buff.write(b, 0, nRead); buff.flush(); return buff.toByteArray(); } /** * Read the specified file into a byte array. * * @param in * The file to read into a byte array. * @return The contents of the file as a byte array. * @throws IOException Thrown by underlying stream. */ public static byte[] readBytes(File in) throws IOException { return readBytes(in, -1); } /** * Read the specified file into a byte array. * * @param in * The file to read into a byte array. * @param maxBytes * The maximum number of bytes to read, or -1 to read all bytes. * @return The contents of the file as a byte array. * @throws IOException Thrown by underlying stream. */ public static byte[] readBytes(File in, int maxBytes) throws IOException { if (in == null || ! (in.exists() && in.canRead())) return new byte[0]; try (FileInputStream is = new FileInputStream(in)) { return readBytes(is, maxBytes); } } /** * Reads the specified input stream into the specified byte array. * * @param in * The input stream to read. *
Can be null. *
Stream is automatically closed. * @return A byte array containing the contents. Never null. * @throws IOException Thrown by underlying stream. */ public static byte[] readBytes(Reader in) throws IOException { if (in == null) return new byte[0]; try (Reader in2 = in) { return read(in2, -1).getBytes(); } } //----------------------------------------------------------------------------------------------------------------- // Other utilities. //----------------------------------------------------------------------------------------------------------------- /** * Wraps the specified reader in a buffered reader. * * @param r The reader being wrapped. * @return * The reader wrapped in a {@link BufferedReader}, or the original {@link Reader} if it's already a buffered * reader. */ public static Reader toBufferedReader(Reader r) { if (r == null || r instanceof BufferedReader || r instanceof StringReader) return r; return new BufferedReader(r); } /** * Counts the number of bytes in the input stream and then closes the stream. * * @param is The input stream to read from. * @return The number of bytes read. * @throws IOException Thrown by underlying stream. */ public static long count(InputStream is) throws IOException { if (is == null) return 0; long c = 0; long i; try { while ((i = is.skip(1024)) != 0) c += i; } finally { is.close(); } return c; } /** * Counts the number of characters in the reader and then closes the reader. * * @param r The reader to read from. * @return The number of characters read. * @throws IOException Thrown by underlying stream. */ public static long count(Reader r) throws IOException { if (r == null) return 0; long c = 0; long i; try { while ((i = r.skip(1024)) != 0) c += i; } finally { r.close(); } return c; } /** * Close input stream and ignore any exceptions. * *

* No-op if input stream is null. * * @param is The input stream to close. */ public static void closeQuietly(InputStream is) { try { if (is != null) is.close(); } catch (IOException e) {} } /** * Close output stream and ignore any exceptions. * *

* No-op if output stream is null. * * @param os The output stream to close. */ public static void closeQuietly(OutputStream os) { try { if (os != null) os.close(); } catch (IOException e) {} } /** * Close reader and ignore any exceptions. * *

* No-op if reader is null. * * @param r The reader to close. */ public static void closeQuietly(Reader r) { try { if (r != null) r.close(); } catch (IOException e) {} } /** * Close writer and ignore any exceptions. * *

* No-op if writer is null. * * @param w The writer to close. */ public static void closeQuietly(Writer w) { try { if (w != null) w.close(); } catch (IOException e) {} } /** * Quietly close all specified input streams, output streams, readers, and writers. * * @param o The list of all objects to quietly close. */ public static void closeQuietly(Object...o) { for (Object o2 : o) { if (o2 instanceof InputStream) closeQuietly((InputStream)o2); if (o2 instanceof OutputStream) closeQuietly((OutputStream)o2); if (o2 instanceof Reader) closeQuietly((Reader)o2); if (o2 instanceof Writer) closeQuietly((Writer)o2); } } /** * Flushes multiple output streams and writers in a single call. * * @param o * The objects to flush. * null entries are ignored. * @throws IOException Thrown by underlying stream. */ public static void flush(Object...o) throws IOException { IOException ex = null; for (Object o2 : o) { try { if (o2 instanceof OutputStream) ((OutputStream)o2).flush(); if (o2 instanceof Writer) ((Writer)o2).flush(); } catch (IOException e) { ex = e; } } if (ex != null) throw ex; } /** * Close all specified input streams, output streams, readers, and writers. * * @param o * The list of all objects to close. * null entries are ignored. * @throws IOException Thrown by underlying stream. */ public static void close(Object...o) throws IOException { IOException ex = null; for (Object o2 : o) { try { if (o2 instanceof InputStream) ((InputStream)o2).close(); if (o2 instanceof OutputStream) ((OutputStream)o2).close(); if (o2 instanceof Reader) ((Reader)o2).close(); if (o2 instanceof Writer) ((Writer)o2).close(); } catch (IOException e) { ex = e; } } if (ex != null) throw ex; } /** * Loads a text file from either the file system or classpath. * * @param name The file name. * @param paths The paths to search. * @return The file contents, or null if not found. * @throws IOException Thrown by underlying stream. */ public static String loadSystemResourceAsString(String name, String...paths) throws IOException { for (String path : paths) { File p = new File(path); if (p.exists()) { File f = new File(p, name); if (f.exists() && f.canRead()) return read(f); } } ClassLoader cl = Thread.currentThread().getContextClassLoader(); if (cl == null) cl = ClassLoader.getSystemClassLoader(); for (String path : paths) { String n = ".".equals(path) ? name : path + '/' + name; try (InputStream is = cl.getResourceAsStream(n)) { if (is != null) return read(is); } try (InputStream is = ClassLoader.getSystemResourceAsStream(n)) { if (is != null) return read(is); } } return null; } private static byte[] byteBuffer(int maxBytes) { if (BYTE_BUFFER_CACHE != null) { byte[] x = BYTE_BUFFER_CACHE.get(); if (x == null) { x = new byte[BUFF_SIZE]; BYTE_BUFFER_CACHE.set(x); BYTE_BUFFER_CACHE_MISSES.incrementAndGet(); } else { BYTE_BUFFER_CACHE_HITS.incrementAndGet(); } return x; } return new byte[buffSize(maxBytes)]; } private static char[] charBuffer(int maxChars) { if (CHAR_BUFFER_CACHE != null) { char[] x = CHAR_BUFFER_CACHE.get(); if (x == null) { x = new char[BUFF_SIZE]; CHAR_BUFFER_CACHE.set(x); CHAR_BUFFER_CACHE_MISSES.incrementAndGet(); } else { CHAR_BUFFER_CACHE_HITS.incrementAndGet(); } return x; } return new char[buffSize(maxChars)]; } private static int buffSize(long max) { return (max > 0 && max < BUFF_SIZE) ? (int)max : BUFF_SIZE; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy