/* * ModeShape (http://www.modeshape.org) * * Licensed 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.modeshape.common.util; import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; import java.io.Closeable; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.Reader; import java.io.Writer; import java.net.MalformedURLException; import java.net.URL; import java.util.Arrays; import org.modeshape.common.annotation.Immutable; import org.modeshape.common.logging.Logger; /** * A set of utilities for more easily performing I/O. */ @Immutable public class IoUtil { private static final Logger LOGGER = Logger.getLogger(IoUtil.class); /** * Read and return the entire contents of the supplied {@link InputStream stream}. This method always closes the stream when * finished reading. * * @param stream the stream to the contents; may be null * @return the contents, or an empty byte array if the supplied reader is null * @throws IOException if there is an error reading the content */ public static byte[] readBytes( InputStream stream ) throws IOException { if (stream == null) return new byte[] {}; byte[] buffer = new byte[1024]; ByteArrayOutputStream output = new ByteArrayOutputStream(); boolean error = false; try { int numRead = 0; while ((numRead = stream.read(buffer)) > -1) { output.write(buffer, 0, numRead); } } catch (IOException e) { error = true; // this error should be thrown, even if there is an error closing stream throw e; } catch (RuntimeException e) { error = true; // this error should be thrown, even if there is an error closing stream throw e; } finally { try { stream.close(); } catch (IOException e) { if (!error) throw e; } } output.flush(); return output.toByteArray(); } /** * Read and return the entire contents of the supplied {@link File file}. * * @param file the file containing the contents; may be null * @return the contents, or an empty byte array if the supplied file is null * @throws IOException if there is an error reading the content */ public static byte[] readBytes( File file ) throws IOException { if (file == null) return new byte[] {}; InputStream stream = new BufferedInputStream(new FileInputStream(file)); boolean error = false; try { return readBytes(stream); } catch (IOException e) { error = true; // this error should be thrown, even if there is an error closing stream throw e; } catch (RuntimeException e) { error = true; // this error should be thrown, even if there is an error closing stream throw e; } finally { try { stream.close(); } catch (IOException e) { if (!error) throw e; } } } /** * Read and return the entire contents of the supplied {@link Reader}. This method always closes the reader when finished * reading. * * @param reader the reader of the contents; may be null * @return the contents, or an empty string if the supplied reader is null * @throws IOException if there is an error reading the content */ public static String read( Reader reader ) throws IOException { if (reader == null) return ""; StringBuilder sb = new StringBuilder(); boolean error = false; try { int numRead = 0; char[] buffer = new char[1024]; while ((numRead = reader.read(buffer)) > -1) { sb.append(buffer, 0, numRead); } } catch (IOException e) { error = true; // this error should be thrown, even if there is an error closing reader throw e; } catch (RuntimeException e) { error = true; // this error should be thrown, even if there is an error closing reader throw e; } finally { try { reader.close(); } catch (IOException e) { if (!error) throw e; } } return sb.toString(); } /** * Read and return the entire contents of the supplied {@link InputStream}. This method always closes the stream when finished * reading. * * @param stream the streamed contents; may be null * @return the contents, or an empty string if the supplied stream is null * @throws IOException if there is an error reading the content */ public static String read( InputStream stream ) throws IOException { return stream == null ? "" : read(new InputStreamReader(stream)); } /** * Read and return the entire contents of the supplied {@link InputStream}. This method always closes the stream when finished * reading. * * @param stream the streamed contents; may be null * @param charset charset of the stream data; may not be null * @return the contents, or an empty string if the supplied stream is null * @throws IOException if there is an error reading the content */ public static String read( InputStream stream, String charset ) throws IOException { return stream == null ? "" : read(new InputStreamReader(stream, charset)); } /** * Read and return the entire contents of the supplied {@link File}. * * @param file the file containing the information to be read; may be null * @return the contents, or an empty string if the supplied reader is null * @throws IOException if there is an error reading the content */ public static String read( File file ) throws IOException { if (file == null) return ""; StringBuilder sb = new StringBuilder(); boolean error = false; Reader reader = new FileReader(file); try { int numRead = 0; char[] buffer = new char[1024]; while ((numRead = reader.read(buffer)) > -1) { sb.append(buffer, 0, numRead); } } catch (IOException e) { error = true; // this error should be thrown, even if there is an error closing reader throw e; } catch (RuntimeException e) { error = true; // this error should be thrown, even if there is an error closing reader throw e; } finally { try { reader.close(); } catch (IOException e) { if (!error) throw e; } } return sb.toString(); } /** * Write the entire contents of the supplied string to the given file. * * @param content the content to write to the stream; may be null * @param file the file to which the content is to be written * @throws IOException * @throws IllegalArgumentException if the stream is null */ public static void write( String content, File file ) throws IOException { CheckArg.isNotNull(file, "destination file"); if (content != null) { write(content, new FileOutputStream(file)); } } /** * Write the entire contents of the supplied string to the given stream. This method always flushes and closes the stream when * finished. * * @param content the content to write to the stream; may be null * @param stream the stream to which the content is to be written * @throws IOException * @throws IllegalArgumentException if the stream is null */ public static void write( String content, OutputStream stream ) throws IOException { CheckArg.isNotNull(stream, "destination stream"); boolean error = false; try { if (content != null) { byte[] bytes = content.getBytes(); stream.write(bytes, 0, bytes.length); } } catch (IOException e) { error = true; // this error should be thrown, even if there is an error flushing/closing stream throw e; } catch (RuntimeException e) { error = true; // this error should be thrown, even if there is an error flushing/closing stream throw e; } finally { try { stream.flush(); } catch (IOException e) { if (!error) throw e; } finally { try { stream.close(); } catch (IOException e) { if (!error) throw e; } } } } /** * Write the entire contents of the supplied string to the given writer. This method always flushes and closes the writer when * finished. * * @param content the content to write to the writer; may be null * @param writer the writer to which the content is to be written * @throws IOException * @throws IllegalArgumentException if the writer is null */ public static void write( String content, Writer writer ) throws IOException { CheckArg.isNotNull(writer, "destination writer"); boolean error = false; try { if (content != null) { writer.write(content); } } catch (IOException e) { error = true; // this error should be thrown, even if there is an error flushing/closing writer throw e; } catch (RuntimeException e) { error = true; // this error should be thrown, even if there is an error flushing/closing writer throw e; } finally { try { writer.flush(); } catch (IOException e) { if (!error) throw e; } finally { try { writer.close(); } catch (IOException e) { if (!error) throw e; } } } } /** * Write the entire contents of the supplied string to the given stream. This method always flushes and closes the stream when * finished. * * @param input the content to write to the stream; may be null * @param stream the stream to which the content is to be written * @throws IOException * @throws IllegalArgumentException if the stream is null */ public static void write( InputStream input, OutputStream stream ) throws IOException { write(input, stream, 1024); } /** * Write the entire contents of the supplied string to the given stream. This method always flushes and closes the stream when * finished. * * @param input the content to write to the stream; may be null * @param stream the stream to which the content is to be written * @param bufferSize the size of the buffer; must be positive * @throws IOException * @throws IllegalArgumentException if the stream is null */ public static void write( InputStream input, OutputStream stream, int bufferSize ) throws IOException { CheckArg.isNotNull(stream, "destination stream"); CheckArg.isPositive(bufferSize, "bufferSize"); boolean error = false; try { if (input != null) { byte[] buffer = new byte[bufferSize]; try { int numRead = 0; while ((numRead = input.read(buffer)) > -1) { stream.write(buffer, 0, numRead); } } finally { input.close(); } } } catch (IOException e) { error = true; // this error should be thrown, even if there is an error flushing/closing stream throw e; } catch (RuntimeException e) { error = true; // this error should be thrown, even if there is an error flushing/closing stream throw e; } finally { try { stream.flush(); } catch (IOException e) { if (!error) throw e; } finally { try { stream.close(); } catch (IOException e) { if (!error) throw e; } } } } /** * Write the entire contents of the supplied string to the given writer. This method always flushes and closes the writer when * finished. * * @param input the content to write to the writer; may be null * @param writer the writer to which the content is to be written * @throws IOException * @throws IllegalArgumentException if the writer is null */ public static void write( Reader input, Writer writer ) throws IOException { CheckArg.isNotNull(writer, "destination writer"); boolean error = false; try { if (input != null) { char[] buffer = new char[1024]; try { int numRead = 0; while ((numRead = input.read(buffer)) > -1) { writer.write(buffer, 0, numRead); } } finally { input.close(); } } } catch (IOException e) { error = true; // this error should be thrown, even if there is an error flushing/closing writer throw e; } catch (RuntimeException e) { error = true; // this error should be thrown, even if there is an error flushing/closing writer throw e; } finally { try { writer.flush(); } catch (IOException e) { if (!error) throw e; } finally { try { writer.close(); } catch (IOException e) { if (!error) throw e; } } } } /** * Write the entire contents of the supplied string to the given stream. This method always flushes and closes the stream when * finished. * * @param input1 the first stream * @param input2 the second stream * @return true if the streams contain the same content, or false otherwise * @throws IOException * @throws IllegalArgumentException if the stream is null */ public static boolean isSame( InputStream input1, InputStream input2 ) throws IOException { CheckArg.isNotNull(input1, "input1"); CheckArg.isNotNull(input2, "input2"); boolean error = false; try { byte[] buffer1 = new byte[1024]; byte[] buffer2 = new byte[1024]; try { int numRead1 = 0; int numRead2 = 0; while (true) { numRead1 = input1.read(buffer1); numRead2 = input2.read(buffer2); if (numRead1 > -1) { if (numRead2 != numRead1) return false; // Otherwise same number of bytes read if (!Arrays.equals(buffer1, buffer2)) return false; // Otherwise same bytes read, so continue ... } else { // Nothing more in stream 1 ... return numRead2 < 0; } } } finally { input1.close(); } } catch (IOException e) { error = true; // this error should be thrown, even if there is an error closing stream 2 throw e; } catch (RuntimeException e) { error = true; // this error should be thrown, even if there is an error closing stream 2 throw e; } finally { try { input2.close(); } catch (IOException e) { if (!error) throw e; } } } /** * Get the {@link InputStream input stream} to the resource given by the supplied path. If a class loader is supplied, the * method attempts to resolve the resource using the {@link ClassLoader#getResourceAsStream(String)} method; if the result is * non-null, it is returned. Otherwise, if a class is supplied, this method attempts to resolve the resource using the * {@link Class#getResourceAsStream(String)} method; if the result is non-null, it is returned. Otherwise, this method then * uses the Class' ClassLoader to load the resource; if non-null, it is returned . Otherwise, this method looks for an * existing and readable {@link File file} at the path; if found, a buffered stream to that file is returned. Otherwise, this * method attempts to parse the resource path into a valid {@link URL}; if this succeeds, the method attempts to open a stream * to that URL. If all of these fail, this method returns null. * * @param resourcePath the logical path to the classpath, file, or URL resource * @param clazz the class that should be used to load the resource as a stream; may be null * @param classLoader the classloader that should be used to load the resource as a stream; may be null * @return an input stream to the resource; or null if the resource could not be found * @throws IllegalArgumentException if the resource path is null or empty */ public static InputStream getResourceAsStream( String resourcePath, ClassLoader classLoader, Class<?> clazz ) { CheckArg.isNotEmpty(resourcePath, "resourcePath"); InputStream result = null; if (classLoader != null) { // Try using the class loader first ... result = classLoader.getResourceAsStream(resourcePath); } if (result == null && clazz != null) { // Not yet found, so try the class ... result = clazz.getResourceAsStream(resourcePath); if (result == null) { // Not yet found, so try the class's class loader ... result = clazz.getClassLoader().getResourceAsStream(resourcePath); } } if (result == null) { // Still not found, so see if this is an existing File ... try { File file = new File(resourcePath); if (file.exists() && file.canRead()) { return new BufferedInputStream(new FileInputStream(file)); } } catch (FileNotFoundException e) { // just continue ... } } if (result == null) { // Still not found, so try to construct a URL out of it ... try { URL url = new URL(resourcePath); return url.openStream(); } catch (MalformedURLException e) { // just continue ... } catch (IOException err) { // just continue ... } } // Couldn't find it anywhere ... return result; } /** * Closes the closable silently. Any exceptions are ignored. * * @param closeable the closeable instance; may be null */ public static void closeQuietly( Closeable closeable ) { if (closeable == null) { return; } try { closeable.close(); } catch (Throwable t) { LOGGER.debug(t, "Ignored error at closing stream"); } } private IoUtil() { // Prevent construction } }