001/* 002 * ModeShape (http://www.modeshape.org) 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.modeshape.common.util; 017 018import java.io.BufferedInputStream; 019import java.io.ByteArrayOutputStream; 020import java.io.Closeable; 021import java.io.File; 022import java.io.FileInputStream; 023import java.io.FileNotFoundException; 024import java.io.FileOutputStream; 025import java.io.FileReader; 026import java.io.IOException; 027import java.io.InputStream; 028import java.io.InputStreamReader; 029import java.io.OutputStream; 030import java.io.Reader; 031import java.io.Writer; 032import java.net.MalformedURLException; 033import java.net.URL; 034import java.util.Arrays; 035import org.modeshape.common.annotation.Immutable; 036import org.modeshape.common.logging.Logger; 037 038/** 039 * A set of utilities for more easily performing I/O. 040 */ 041@Immutable 042public class IoUtil { 043 044 private static final Logger LOGGER = Logger.getLogger(IoUtil.class); 045 046 /** 047 * Read and return the entire contents of the supplied {@link InputStream stream}. This method always closes the stream when 048 * finished reading. 049 * 050 * @param stream the stream to the contents; may be null 051 * @return the contents, or an empty byte array if the supplied reader is null 052 * @throws IOException if there is an error reading the content 053 */ 054 public static byte[] readBytes( InputStream stream ) throws IOException { 055 if (stream == null) return new byte[] {}; 056 byte[] buffer = new byte[1024]; 057 ByteArrayOutputStream output = new ByteArrayOutputStream(); 058 boolean error = false; 059 try { 060 int numRead = 0; 061 while ((numRead = stream.read(buffer)) > -1) { 062 output.write(buffer, 0, numRead); 063 } 064 } catch (IOException e) { 065 error = true; // this error should be thrown, even if there is an error closing stream 066 throw e; 067 } catch (RuntimeException e) { 068 error = true; // this error should be thrown, even if there is an error closing stream 069 throw e; 070 } finally { 071 try { 072 stream.close(); 073 } catch (IOException e) { 074 if (!error) throw e; 075 } 076 } 077 output.flush(); 078 return output.toByteArray(); 079 } 080 081 /** 082 * Read and return the entire contents of the supplied {@link File file}. 083 * 084 * @param file the file containing the contents; may be null 085 * @return the contents, or an empty byte array if the supplied file is null 086 * @throws IOException if there is an error reading the content 087 */ 088 public static byte[] readBytes( File file ) throws IOException { 089 if (file == null) return new byte[] {}; 090 InputStream stream = new BufferedInputStream(new FileInputStream(file)); 091 boolean error = false; 092 try { 093 return readBytes(stream); 094 } catch (IOException e) { 095 error = true; // this error should be thrown, even if there is an error closing stream 096 throw e; 097 } catch (RuntimeException e) { 098 error = true; // this error should be thrown, even if there is an error closing stream 099 throw e; 100 } finally { 101 try { 102 stream.close(); 103 } catch (IOException e) { 104 if (!error) throw e; 105 } 106 } 107 } 108 109 /** 110 * Read and return the entire contents of the supplied {@link Reader}. This method always closes the reader when finished 111 * reading. 112 * 113 * @param reader the reader of the contents; may be null 114 * @return the contents, or an empty string if the supplied reader is null 115 * @throws IOException if there is an error reading the content 116 */ 117 public static String read( Reader reader ) throws IOException { 118 if (reader == null) return ""; 119 StringBuilder sb = new StringBuilder(); 120 boolean error = false; 121 try { 122 int numRead = 0; 123 char[] buffer = new char[1024]; 124 while ((numRead = reader.read(buffer)) > -1) { 125 sb.append(buffer, 0, numRead); 126 } 127 } catch (IOException e) { 128 error = true; // this error should be thrown, even if there is an error closing reader 129 throw e; 130 } catch (RuntimeException e) { 131 error = true; // this error should be thrown, even if there is an error closing reader 132 throw e; 133 } finally { 134 try { 135 reader.close(); 136 } catch (IOException e) { 137 if (!error) throw e; 138 } 139 } 140 return sb.toString(); 141 } 142 143 /** 144 * Read and return the entire contents of the supplied {@link InputStream}. This method always closes the stream when finished 145 * reading. 146 * 147 * @param stream the streamed contents; may be null 148 * @return the contents, or an empty string if the supplied stream is null 149 * @throws IOException if there is an error reading the content 150 */ 151 public static String read( InputStream stream ) throws IOException { 152 return stream == null ? "" : read(new InputStreamReader(stream)); 153 } 154 155 /** 156 * Read and return the entire contents of the supplied {@link InputStream}. This method always closes the stream when finished 157 * reading. 158 * 159 * @param stream the streamed contents; may be null 160 * @param charset charset of the stream data; may not be null 161 * @return the contents, or an empty string if the supplied stream is null 162 * @throws IOException if there is an error reading the content 163 */ 164 public static String read( InputStream stream, 165 String charset ) throws IOException { 166 return stream == null ? "" : read(new InputStreamReader(stream, charset)); 167 } 168 169 /** 170 * Read and return the entire contents of the supplied {@link File}. 171 * 172 * @param file the file containing the information to be read; may be null 173 * @return the contents, or an empty string if the supplied reader is null 174 * @throws IOException if there is an error reading the content 175 */ 176 public static String read( File file ) throws IOException { 177 if (file == null) return ""; 178 StringBuilder sb = new StringBuilder(); 179 boolean error = false; 180 Reader reader = new FileReader(file); 181 try { 182 int numRead = 0; 183 char[] buffer = new char[1024]; 184 while ((numRead = reader.read(buffer)) > -1) { 185 sb.append(buffer, 0, numRead); 186 } 187 } catch (IOException e) { 188 error = true; // this error should be thrown, even if there is an error closing reader 189 throw e; 190 } catch (RuntimeException e) { 191 error = true; // this error should be thrown, even if there is an error closing reader 192 throw e; 193 } finally { 194 try { 195 reader.close(); 196 } catch (IOException e) { 197 if (!error) throw e; 198 } 199 } 200 return sb.toString(); 201 } 202 203 /** 204 * Write the entire contents of the supplied string to the given file. 205 * 206 * @param content the content to write to the stream; may be null 207 * @param file the file to which the content is to be written 208 * @throws IOException 209 * @throws IllegalArgumentException if the stream is null 210 */ 211 public static void write( String content, 212 File file ) throws IOException { 213 CheckArg.isNotNull(file, "destination file"); 214 if (content != null) { 215 write(content, new FileOutputStream(file)); 216 } 217 } 218 219 /** 220 * Write the entire contents of the supplied string to the given stream. This method always flushes and closes the stream when 221 * finished. 222 * 223 * @param content the content to write to the stream; may be null 224 * @param stream the stream to which the content is to be written 225 * @throws IOException 226 * @throws IllegalArgumentException if the stream is null 227 */ 228 public static void write( String content, 229 OutputStream stream ) throws IOException { 230 CheckArg.isNotNull(stream, "destination stream"); 231 boolean error = false; 232 try { 233 if (content != null) { 234 byte[] bytes = content.getBytes(); 235 stream.write(bytes, 0, bytes.length); 236 } 237 } catch (IOException e) { 238 error = true; // this error should be thrown, even if there is an error flushing/closing stream 239 throw e; 240 } catch (RuntimeException e) { 241 error = true; // this error should be thrown, even if there is an error flushing/closing stream 242 throw e; 243 } finally { 244 try { 245 stream.flush(); 246 } catch (IOException e) { 247 if (!error) throw e; 248 } finally { 249 try { 250 stream.close(); 251 } catch (IOException e) { 252 if (!error) throw e; 253 } 254 } 255 } 256 } 257 258 /** 259 * Write the entire contents of the supplied string to the given writer. This method always flushes and closes the writer when 260 * finished. 261 * 262 * @param content the content to write to the writer; may be null 263 * @param writer the writer to which the content is to be written 264 * @throws IOException 265 * @throws IllegalArgumentException if the writer is null 266 */ 267 public static void write( String content, 268 Writer writer ) throws IOException { 269 CheckArg.isNotNull(writer, "destination writer"); 270 boolean error = false; 271 try { 272 if (content != null) { 273 writer.write(content); 274 } 275 } catch (IOException e) { 276 error = true; // this error should be thrown, even if there is an error flushing/closing writer 277 throw e; 278 } catch (RuntimeException e) { 279 error = true; // this error should be thrown, even if there is an error flushing/closing writer 280 throw e; 281 } finally { 282 try { 283 writer.flush(); 284 } catch (IOException e) { 285 if (!error) throw e; 286 } finally { 287 try { 288 writer.close(); 289 } catch (IOException e) { 290 if (!error) throw e; 291 } 292 } 293 } 294 } 295 296 /** 297 * Write the entire contents of the supplied string to the given stream. This method always flushes and closes the stream when 298 * finished. 299 * 300 * @param input the content to write to the stream; may be null 301 * @param stream the stream to which the content is to be written 302 * @throws IOException 303 * @throws IllegalArgumentException if the stream is null 304 */ 305 public static void write( InputStream input, 306 OutputStream stream ) throws IOException { 307 write(input, stream, 1024); 308 } 309 310 /** 311 * Write the entire contents of the supplied string to the given stream. This method always flushes and closes the stream when 312 * finished. 313 * 314 * @param input the content to write to the stream; may be null 315 * @param stream the stream to which the content is to be written 316 * @param bufferSize the size of the buffer; must be positive 317 * @throws IOException 318 * @throws IllegalArgumentException if the stream is null 319 */ 320 public static void write( InputStream input, 321 OutputStream stream, 322 int bufferSize ) throws IOException { 323 CheckArg.isNotNull(stream, "destination stream"); 324 CheckArg.isPositive(bufferSize, "bufferSize"); 325 boolean error = false; 326 try { 327 if (input != null) { 328 byte[] buffer = new byte[bufferSize]; 329 try { 330 int numRead = 0; 331 while ((numRead = input.read(buffer)) > -1) { 332 stream.write(buffer, 0, numRead); 333 } 334 } finally { 335 input.close(); 336 } 337 } 338 } catch (IOException e) { 339 error = true; // this error should be thrown, even if there is an error flushing/closing stream 340 throw e; 341 } catch (RuntimeException e) { 342 error = true; // this error should be thrown, even if there is an error flushing/closing stream 343 throw e; 344 } finally { 345 try { 346 stream.flush(); 347 } catch (IOException e) { 348 if (!error) throw e; 349 } finally { 350 try { 351 stream.close(); 352 } catch (IOException e) { 353 if (!error) throw e; 354 } 355 } 356 } 357 } 358 359 /** 360 * Write the entire contents of the supplied string to the given writer. This method always flushes and closes the writer when 361 * finished. 362 * 363 * @param input the content to write to the writer; may be null 364 * @param writer the writer to which the content is to be written 365 * @throws IOException 366 * @throws IllegalArgumentException if the writer is null 367 */ 368 public static void write( Reader input, 369 Writer writer ) throws IOException { 370 CheckArg.isNotNull(writer, "destination writer"); 371 boolean error = false; 372 try { 373 if (input != null) { 374 char[] buffer = new char[1024]; 375 try { 376 int numRead = 0; 377 while ((numRead = input.read(buffer)) > -1) { 378 writer.write(buffer, 0, numRead); 379 } 380 } finally { 381 input.close(); 382 } 383 } 384 } catch (IOException e) { 385 error = true; // this error should be thrown, even if there is an error flushing/closing writer 386 throw e; 387 } catch (RuntimeException e) { 388 error = true; // this error should be thrown, even if there is an error flushing/closing writer 389 throw e; 390 } finally { 391 try { 392 writer.flush(); 393 } catch (IOException e) { 394 if (!error) throw e; 395 } finally { 396 try { 397 writer.close(); 398 } catch (IOException e) { 399 if (!error) throw e; 400 } 401 } 402 } 403 } 404 405 /** 406 * Write the entire contents of the supplied string to the given stream. This method always flushes and closes the stream when 407 * finished. 408 * 409 * @param input1 the first stream 410 * @param input2 the second stream 411 * @return true if the streams contain the same content, or false otherwise 412 * @throws IOException 413 * @throws IllegalArgumentException if the stream is null 414 */ 415 public static boolean isSame( InputStream input1, 416 InputStream input2 ) throws IOException { 417 CheckArg.isNotNull(input1, "input1"); 418 CheckArg.isNotNull(input2, "input2"); 419 boolean error = false; 420 try { 421 byte[] buffer1 = new byte[1024]; 422 byte[] buffer2 = new byte[1024]; 423 try { 424 int numRead1 = 0; 425 int numRead2 = 0; 426 while (true) { 427 numRead1 = input1.read(buffer1); 428 numRead2 = input2.read(buffer2); 429 if (numRead1 > -1) { 430 if (numRead2 != numRead1) return false; 431 // Otherwise same number of bytes read 432 if (!Arrays.equals(buffer1, buffer2)) return false; 433 // Otherwise same bytes read, so continue ... 434 } else { 435 // Nothing more in stream 1 ... 436 return numRead2 < 0; 437 } 438 } 439 } finally { 440 input1.close(); 441 } 442 } catch (IOException e) { 443 error = true; // this error should be thrown, even if there is an error closing stream 2 444 throw e; 445 } catch (RuntimeException e) { 446 error = true; // this error should be thrown, even if there is an error closing stream 2 447 throw e; 448 } finally { 449 try { 450 input2.close(); 451 } catch (IOException e) { 452 if (!error) throw e; 453 } 454 } 455 } 456 457 /** 458 * Get the {@link InputStream input stream} to the resource given by the supplied path. If a class loader is supplied, the 459 * method attempts to resolve the resource using the {@link ClassLoader#getResourceAsStream(String)} method; if the result is 460 * non-null, it is returned. Otherwise, if a class is supplied, this method attempts to resolve the resource using the 461 * {@link Class#getResourceAsStream(String)} method; if the result is non-null, it is returned. Otherwise, this method then 462 * uses the Class' ClassLoader to load the resource; if non-null, it is returned . Otherwise, this method looks for an 463 * existing and readable {@link File file} at the path; if found, a buffered stream to that file is returned. Otherwise, this 464 * method attempts to parse the resource path into a valid {@link URL}; if this succeeds, the method attempts to open a stream 465 * to that URL. If all of these fail, this method returns null. 466 * 467 * @param resourcePath the logical path to the classpath, file, or URL resource 468 * @param clazz the class that should be used to load the resource as a stream; may be null 469 * @param classLoader the classloader that should be used to load the resource as a stream; may be null 470 * @return an input stream to the resource; or null if the resource could not be found 471 * @throws IllegalArgumentException if the resource path is null or empty 472 */ 473 public static InputStream getResourceAsStream( String resourcePath, 474 ClassLoader classLoader, 475 Class<?> clazz ) { 476 CheckArg.isNotEmpty(resourcePath, "resourcePath"); 477 InputStream result = null; 478 if (classLoader != null) { 479 // Try using the class loader first ... 480 result = classLoader.getResourceAsStream(resourcePath); 481 } 482 if (result == null && clazz != null) { 483 // Not yet found, so try the class ... 484 result = clazz.getResourceAsStream(resourcePath); 485 if (result == null) { 486 // Not yet found, so try the class's class loader ... 487 result = clazz.getClassLoader().getResourceAsStream(resourcePath); 488 } 489 } 490 if (result == null) { 491 // Still not found, so see if this is an existing File ... 492 try { 493 File file = new File(resourcePath); 494 if (file.exists() && file.canRead()) { 495 return new BufferedInputStream(new FileInputStream(file)); 496 } 497 } catch (FileNotFoundException e) { 498 // just continue ... 499 } 500 } 501 if (result == null) { 502 // Still not found, so try to construct a URL out of it ... 503 try { 504 URL url = new URL(resourcePath); 505 return url.openStream(); 506 } catch (MalformedURLException e) { 507 // just continue ... 508 } catch (IOException err) { 509 // just continue ... 510 } 511 } 512 // Couldn't find it anywhere ... 513 return result; 514 } 515 516 /** 517 * Closes the closable silently. Any exceptions are ignored. 518 * 519 * @param closeable the closeable instance; may be null 520 */ 521 public static void closeQuietly( Closeable closeable ) { 522 if (closeable == null) { 523 return; 524 } 525 try { 526 closeable.close(); 527 } catch (Throwable t) { 528 LOGGER.debug(t, "Ignored error at closing stream"); 529 } 530 } 531 532 private IoUtil() { 533 // Prevent construction 534 } 535}