//
// ========================================================================
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under
// the terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0
//
// This Source Code may also be made available under the following
// Secondary Licenses when the conditions for such availability set
// forth in the Eclipse Public License, v. 2.0 are satisfied:
// the Apache License v2.0 which is available at
// https://www.apache.org/licenses/LICENSE-2.0
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//

package org.eclipse.jetty.util;

import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
import java.nio.ByteBuffer;
import java.nio.channels.GatheringByteChannel;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

IO Utilities. Provides stream handling utilities in singleton Threadpool implementation accessed by static members.
/** * IO Utilities. * Provides stream handling utilities in * singleton Threadpool implementation accessed by static members. */
public class IO { private static final Logger LOG = LoggerFactory.getLogger(IO.class); public static final String CRLF = "\r\n"; public static final byte[] CRLF_BYTES = {(byte)'\r', (byte)'\n'}; public static final int bufferSize = 64 * 1024; static class Job implements Runnable { InputStream in; OutputStream out; Reader read; Writer write; Job(InputStream in, OutputStream out) { this.in = in; this.out = out; this.read = null; this.write = null; } Job(Reader read, Writer write) { this.in = null; this.out = null; this.read = read; this.write = write; } @Override public void run() { try { if (in != null) copy(in, out, -1); else copy(read, write, -1); } catch (IOException e) { LOG.trace("IGNORED", e); try { if (out != null) out.close(); if (write != null) write.close(); } catch (IOException e2) { LOG.trace("IGNORED", e2); } } } }
Copy Stream in to Stream out until EOF or exception.
Params:
  • in – the input stream to read from (until EOF)
  • out – the output stream to write to
Throws:
/** * Copy Stream in to Stream out until EOF or exception. * * @param in the input stream to read from (until EOF) * @param out the output stream to write to * @throws IOException if unable to copy streams */
public static void copy(InputStream in, OutputStream out) throws IOException { copy(in, out, -1); }
Copy Reader to Writer out until EOF or exception.
Params:
  • in – the read to read from (until EOF)
  • out – the writer to write to
Throws:
/** * Copy Reader to Writer out until EOF or exception. * * @param in the read to read from (until EOF) * @param out the writer to write to * @throws IOException if unable to copy the streams */
public static void copy(Reader in, Writer out) throws IOException { copy(in, out, -1); }
Copy Stream in to Stream for byteCount bytes or until EOF or exception.
Params:
  • in – the stream to read from
  • out – the stream to write to
  • byteCount – the number of bytes to copy
Throws:
/** * Copy Stream in to Stream for byteCount bytes or until EOF or exception. * * @param in the stream to read from * @param out the stream to write to * @param byteCount the number of bytes to copy * @throws IOException if unable to copy the streams */
public static void copy(InputStream in, OutputStream out, long byteCount) throws IOException { byte[] buffer = new byte[bufferSize]; int len = bufferSize; if (byteCount >= 0) { while (byteCount > 0) { int max = byteCount < bufferSize ? (int)byteCount : bufferSize; len = in.read(buffer, 0, max); if (len == -1) break; byteCount -= len; out.write(buffer, 0, len); } } else { while (true) { len = in.read(buffer, 0, bufferSize); if (len < 0) break; out.write(buffer, 0, len); } } }
Copy Reader to Writer for byteCount bytes or until EOF or exception.
Params:
  • in – the Reader to read from
  • out – the Writer to write to
  • byteCount – the number of bytes to copy
Throws:
/** * Copy Reader to Writer for byteCount bytes or until EOF or exception. * * @param in the Reader to read from * @param out the Writer to write to * @param byteCount the number of bytes to copy * @throws IOException if unable to copy streams */
public static void copy(Reader in, Writer out, long byteCount) throws IOException { char[] buffer = new char[bufferSize]; int len = bufferSize; if (byteCount >= 0) { while (byteCount > 0) { if (byteCount < bufferSize) len = in.read(buffer, 0, (int)byteCount); else len = in.read(buffer, 0, bufferSize); if (len == -1) break; byteCount -= len; out.write(buffer, 0, len); } } else if (out instanceof PrintWriter) { PrintWriter pout = (PrintWriter)out; while (!pout.checkError()) { len = in.read(buffer, 0, bufferSize); if (len == -1) break; out.write(buffer, 0, len); } } else { while (true) { len = in.read(buffer, 0, bufferSize); if (len == -1) break; out.write(buffer, 0, len); } } }
Copy files or directories
Params:
  • from – the file to copy
  • to – the destination to copy to
Throws:
/** * Copy files or directories * * @param from the file to copy * @param to the destination to copy to * @throws IOException if unable to copy */
public static void copy(File from, File to) throws IOException { if (from.isDirectory()) copyDir(from, to); else copyFile(from, to); } public static void copyDir(File from, File to) throws IOException { if (to.exists()) { if (!to.isDirectory()) throw new IllegalArgumentException(to.toString()); } else to.mkdirs(); File[] files = from.listFiles(); if (files != null) { for (int i = 0; i < files.length; i++) { String name = files[i].getName(); if (".".equals(name) || "..".equals(name)) continue; copy(files[i], new File(to, name)); } } } public static void copyFile(File from, File to) throws IOException { try (InputStream in = new FileInputStream(from); OutputStream out = new FileOutputStream(to)) { copy(in, out); } }
Read Path to string.
Params:
  • path – the path to read from (until EOF)
  • charset – the charset to read with
Throws:
  • IOException – if unable to read the path (or handle the charset)
Returns:the String parsed from path (default Charset)
/** * Read Path to string. * * @param path the path to read from (until EOF) * @param charset the charset to read with * @return the String parsed from path (default Charset) * @throws IOException if unable to read the path (or handle the charset) */
public static String toString(Path path, Charset charset) throws IOException { byte[] buf = Files.readAllBytes(path); return new String(buf, charset); }
Read input stream to string.
Params:
  • in – the stream to read from (until EOF)
Throws:
  • IOException – if unable to read the stream (or handle the charset)
Returns:the String parsed from stream (default Charset)
/** * Read input stream to string. * * @param in the stream to read from (until EOF) * @return the String parsed from stream (default Charset) * @throws IOException if unable to read the stream (or handle the charset) */
public static String toString(InputStream in) throws IOException { return toString(in, (Charset)null); }
Read input stream to string.
Params:
  • in – the stream to read from (until EOF)
  • encoding – the encoding to use (can be null to use default Charset)
Throws:
  • IOException – if unable to read the stream (or handle the charset)
Returns:the String parsed from the stream
/** * Read input stream to string. * * @param in the stream to read from (until EOF) * @param encoding the encoding to use (can be null to use default Charset) * @return the String parsed from the stream * @throws IOException if unable to read the stream (or handle the charset) */
public static String toString(InputStream in, String encoding) throws IOException { return toString(in, encoding == null ? null : Charset.forName(encoding)); }
Read input stream to string.
Params:
  • in – the stream to read from (until EOF)
  • encoding – the Charset to use (can be null to use default Charset)
Throws:
  • IOException – if unable to read the stream (or handle the charset)
Returns:the String parsed from the stream
/** * Read input stream to string. * * @param in the stream to read from (until EOF) * @param encoding the Charset to use (can be null to use default Charset) * @return the String parsed from the stream * @throws IOException if unable to read the stream (or handle the charset) */
public static String toString(InputStream in, Charset encoding) throws IOException { StringWriter writer = new StringWriter(); InputStreamReader reader = encoding == null ? new InputStreamReader(in) : new InputStreamReader(in, encoding); copy(reader, writer); return writer.toString(); }
Read input stream to string.
Params:
  • in – the reader to read from (until EOF)
Throws:
  • IOException – if unable to read the stream (or handle the charset)
Returns:the String parsed from the reader
/** * Read input stream to string. * * @param in the reader to read from (until EOF) * @return the String parsed from the reader * @throws IOException if unable to read the stream (or handle the charset) */
public static String toString(Reader in) throws IOException { StringWriter writer = new StringWriter(); copy(in, writer); return writer.toString(); }
Delete File. This delete will recursively delete directories - BE CAREFUL
Params:
  • file – The file (or directory) to be deleted.
Returns:true if file was deleted, or directory referenced was deleted. false if file doesn't exist, or was null.
/** * Delete File. * This delete will recursively delete directories - BE CAREFUL * * @param file The file (or directory) to be deleted. * @return true if file was deleted, or directory referenced was deleted. * false if file doesn't exist, or was null. */
public static boolean delete(File file) { if (file == null) return false; if (!file.exists()) return false; if (file.isDirectory()) { File[] files = file.listFiles(); for (int i = 0; files != null && i < files.length; i++) { delete(files[i]); } } return file.delete(); }
Test if directory is empty.
Params:
  • dir – the directory
Returns:true if directory is null, doesn't exist, or has no content. false if not a directory, or has contents
/** * Test if directory is empty. * * @param dir the directory * @return true if directory is null, doesn't exist, or has no content. * false if not a directory, or has contents */
public static boolean isEmptyDir(File dir) { if (dir == null) return true; if (!dir.exists()) return true; if (!dir.isDirectory()) return false; String[] list = dir.list(); if (list == null) return true; return list.length <= 0; }
Closes an arbitrary closable, and logs exceptions at ignore level
Params:
  • closeable – the closeable to close
/** * Closes an arbitrary closable, and logs exceptions at ignore level * * @param closeable the closeable to close */
public static void close(Closeable closeable) { try { if (closeable != null) closeable.close(); } catch (IOException ignore) { LOG.trace("IGNORED", ignore); } }
closes an input stream, and logs exceptions
Params:
  • is – the input stream to close
/** * closes an input stream, and logs exceptions * * @param is the input stream to close */
public static void close(InputStream is) { close((Closeable)is); }
closes an output stream, and logs exceptions
Params:
  • os – the output stream to close
/** * closes an output stream, and logs exceptions * * @param os the output stream to close */
public static void close(OutputStream os) { close((Closeable)os); }
closes a reader, and logs exceptions
Params:
  • reader – the reader to close
/** * closes a reader, and logs exceptions * * @param reader the reader to close */
public static void close(Reader reader) { close((Closeable)reader); }
closes a writer, and logs exceptions
Params:
  • writer – the writer to close
/** * closes a writer, and logs exceptions * * @param writer the writer to close */
public static void close(Writer writer) { close((Closeable)writer); } public static byte[] readBytes(InputStream in) throws IOException { ByteArrayOutputStream bout = new ByteArrayOutputStream(); copy(in, bout); return bout.toByteArray(); }
A gathering write utility wrapper.

This method wraps a gather write with a loop that handles the limitations of some operating systems that have a limit on the number of buffers written. The method loops on the write until either all the content is written or no progress is made.

Params:
  • out – The GatheringByteChannel to write to
  • buffers – The buffers to write
  • offset – The offset into the buffers array
  • length – The length in buffers to write
Throws:
  • IOException – if unable write to the GatheringByteChannel
Returns:The total bytes written
/** * A gathering write utility wrapper. * <p> * This method wraps a gather write with a loop that handles the limitations of some operating systems that have a * limit on the number of buffers written. The method loops on the write until either all the content is written or * no progress is made. * * @param out The GatheringByteChannel to write to * @param buffers The buffers to write * @param offset The offset into the buffers array * @param length The length in buffers to write * @return The total bytes written * @throws IOException if unable write to the GatheringByteChannel */
public static long write(GatheringByteChannel out, ByteBuffer[] buffers, int offset, int length) throws IOException { long total = 0; write: while (length > 0) { // Write as much as we can long wrote = out.write(buffers, offset, length); // If we can't write any more, give up if (wrote == 0) break; // count the total total += wrote; // Look for unwritten content for (int i = offset; i < buffers.length; i++) { if (buffers[i].hasRemaining()) { // loop with new offset and length; length = length - (i - offset); offset = i; continue write; } } length = 0; } return total; }
Returns:An outputstream to nowhere
/** * @return An outputstream to nowhere */
public static OutputStream getNullStream() { return __nullStream; }
Returns:An outputstream to nowhere
/** * @return An outputstream to nowhere */
public static InputStream getClosedStream() { return __closedStream; } private static class NullOS extends OutputStream { @Override public void close() { } @Override public void flush() { } @Override public void write(byte[] b) { } @Override public void write(byte[] b, int i, int l) { } @Override public void write(int b) { } } private static NullOS __nullStream = new NullOS(); private static class ClosedIS extends InputStream { @Override public int read() throws IOException { return -1; } } private static ClosedIS __closedStream = new ClosedIS();
Returns:An writer to nowhere
/** * @return An writer to nowhere */
public static Writer getNullWriter() { return __nullWriter; }
Returns:An writer to nowhere
/** * @return An writer to nowhere */
public static PrintWriter getNullPrintWriter() { return __nullPrintWriter; } private static class NullWrite extends Writer { @Override public void close() { } @Override public void flush() { } @Override public void write(char[] b) { } @Override public void write(char[] b, int o, int l) { } @Override public void write(int b) { } @Override public void write(String s) { } @Override public void write(String s, int o, int l) { } } private static NullWrite __nullWriter = new NullWrite(); private static PrintWriter __nullPrintWriter = new PrintWriter(__nullWriter); }