View Javadoc

1   /*
2    * ModeShape (http://www.modeshape.org)
3    * See the COPYRIGHT.txt file distributed with this work for information
4    * regarding copyright ownership.  Some portions may be licensed
5    * to Red Hat, Inc. under one or more contributor license agreements.
6    * See the AUTHORS.txt file in the distribution for a full listing of 
7    * individual contributors. 
8    *
9    * ModeShape is free software. Unless otherwise indicated, all code in ModeShape
10   * is licensed to you under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation; either version 2.1 of
12   * the License, or (at your option) any later version.
13   *
14   * ModeShape is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17   * Lesser General Public License for more details.
18   *
19   * You should have received a copy of the GNU Lesser General Public
20   * License along with this software; if not, write to the Free
21   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
23   */
24  package org.modeshape.common.util;
25  
26  import java.io.BufferedInputStream;
27  import java.io.ByteArrayOutputStream;
28  import java.io.File;
29  import java.io.FileInputStream;
30  import java.io.FileNotFoundException;
31  import java.io.FileReader;
32  import java.io.IOException;
33  import java.io.InputStream;
34  import java.io.InputStreamReader;
35  import java.io.OutputStream;
36  import java.io.Reader;
37  import java.io.Writer;
38  import java.net.MalformedURLException;
39  import java.net.URL;
40  import java.util.Arrays;
41  import net.jcip.annotations.Immutable;
42  
43  /**
44   * A set of utilities for more easily performing I/O.
45   */
46  @Immutable
47  public class IoUtil {
48  
49      /**
50       * Read and return the entire contents of the supplied {@link InputStream stream}. This method always closes the stream when
51       * finished reading.
52       * 
53       * @param stream the stream to the contents; may be null
54       * @return the contents, or an empty byte array if the supplied reader is null
55       * @throws IOException if there is an error reading the content
56       */
57      public static byte[] readBytes( InputStream stream ) throws IOException {
58          if (stream == null) return new byte[] {};
59          byte[] buffer = new byte[1024];
60          ByteArrayOutputStream output = new ByteArrayOutputStream();
61          boolean error = false;
62          try {
63              int numRead = 0;
64              while ((numRead = stream.read(buffer)) > -1) {
65                  output.write(buffer, 0, numRead);
66              }
67          } catch (IOException e) {
68              error = true; // this error should be thrown, even if there is an error closing stream
69              throw e;
70          } catch (RuntimeException e) {
71              error = true; // this error should be thrown, even if there is an error closing stream
72              throw e;
73          } finally {
74              try {
75                  stream.close();
76              } catch (IOException e) {
77                  if (!error) throw e;
78              }
79          }
80          output.flush();
81          return output.toByteArray();
82      }
83  
84      /**
85       * Read and return the entire contents of the supplied {@link File file}.
86       * 
87       * @param file the file containing the contents; may be null
88       * @return the contents, or an empty byte array if the supplied file is null
89       * @throws IOException if there is an error reading the content
90       */
91      public static byte[] readBytes( File file ) throws IOException {
92          if (file == null) return new byte[] {};
93          InputStream stream = new BufferedInputStream(new FileInputStream(file));
94          boolean error = false;
95          try {
96              return readBytes(stream);
97          } catch (IOException e) {
98              error = true; // this error should be thrown, even if there is an error closing stream
99              throw e;
100         } catch (RuntimeException e) {
101             error = true; // this error should be thrown, even if there is an error closing stream
102             throw e;
103         } finally {
104             try {
105                 stream.close();
106             } catch (IOException e) {
107                 if (!error) throw e;
108             }
109         }
110     }
111 
112     /**
113      * Read and return the entire contents of the supplied {@link Reader}. This method always closes the reader when finished
114      * reading.
115      * 
116      * @param reader the reader of the contents; may be null
117      * @return the contents, or an empty string if the supplied reader is null
118      * @throws IOException if there is an error reading the content
119      */
120     public static String read( Reader reader ) throws IOException {
121         if (reader == null) return "";
122         StringBuilder sb = new StringBuilder();
123         boolean error = false;
124         try {
125             int numRead = 0;
126             char[] buffer = new char[1024];
127             while ((numRead = reader.read(buffer)) > -1) {
128                 sb.append(buffer, 0, numRead);
129             }
130         } catch (IOException e) {
131             error = true; // this error should be thrown, even if there is an error closing reader
132             throw e;
133         } catch (RuntimeException e) {
134             error = true; // this error should be thrown, even if there is an error closing reader
135             throw e;
136         } finally {
137             try {
138                 reader.close();
139             } catch (IOException e) {
140                 if (!error) throw e;
141             }
142         }
143         return sb.toString();
144     }
145 
146     /**
147      * Read and return the entire contents of the supplied {@link InputStream}. This method always closes the stream when finished
148      * reading.
149      * 
150      * @param stream the streamed contents; may be null
151      * @return the contents, or an empty string if the supplied stream is null
152      * @throws IOException if there is an error reading the content
153      */
154     public static String read( InputStream stream ) throws IOException {
155         return stream == null ? "" : read(new InputStreamReader(stream));
156     }
157 
158     /**
159      * Read and return the entire contents of the supplied {@link File}.
160      * 
161      * @param file the file containing the information to be read; may be null
162      * @return the contents, or an empty string if the supplied reader is null
163      * @throws IOException if there is an error reading the content
164      */
165     public static String read( File file ) throws IOException {
166         if (file == null) return "";
167         StringBuilder sb = new StringBuilder();
168         boolean error = false;
169         Reader reader = new FileReader(file);
170         try {
171             int numRead = 0;
172             char[] buffer = new char[1024];
173             while ((numRead = reader.read(buffer)) > -1) {
174                 sb.append(buffer, 0, numRead);
175             }
176         } catch (IOException e) {
177             error = true; // this error should be thrown, even if there is an error closing reader
178             throw e;
179         } catch (RuntimeException e) {
180             error = true; // this error should be thrown, even if there is an error closing reader
181             throw e;
182         } finally {
183             try {
184                 reader.close();
185             } catch (IOException e) {
186                 if (!error) throw e;
187             }
188         }
189         return sb.toString();
190     }
191 
192     /**
193      * Write the entire contents of the supplied string to the given stream. This method always flushes and closes the stream when
194      * finished.
195      * 
196      * @param content the content to write to the stream; may be null
197      * @param stream the stream to which the content is to be written
198      * @throws IOException
199      * @throws IllegalArgumentException if the stream is null
200      */
201     public static void write( String content,
202                               OutputStream stream ) throws IOException {
203         CheckArg.isNotNull(stream, "destination stream");
204         boolean error = false;
205         try {
206             if (content != null) {
207                 byte[] bytes = content.getBytes();
208                 stream.write(bytes, 0, bytes.length);
209             }
210         } catch (IOException e) {
211             error = true; // this error should be thrown, even if there is an error flushing/closing stream
212             throw e;
213         } catch (RuntimeException e) {
214             error = true; // this error should be thrown, even if there is an error flushing/closing stream
215             throw e;
216         } finally {
217             try {
218                 stream.flush();
219             } catch (IOException e) {
220                 if (!error) throw e;
221             } finally {
222                 try {
223                     stream.close();
224                 } catch (IOException e) {
225                     if (!error) throw e;
226                 }
227             }
228         }
229     }
230 
231     /**
232      * Write the entire contents of the supplied string to the given writer. This method always flushes and closes the writer when
233      * finished.
234      * 
235      * @param content the content to write to the writer; may be null
236      * @param writer the writer to which the content is to be written
237      * @throws IOException
238      * @throws IllegalArgumentException if the writer is null
239      */
240     public static void write( String content,
241                               Writer writer ) throws IOException {
242         CheckArg.isNotNull(writer, "destination writer");
243         boolean error = false;
244         try {
245             if (content != null) {
246                 writer.write(content);
247             }
248         } catch (IOException e) {
249             error = true; // this error should be thrown, even if there is an error flushing/closing writer
250             throw e;
251         } catch (RuntimeException e) {
252             error = true; // this error should be thrown, even if there is an error flushing/closing writer
253             throw e;
254         } finally {
255             try {
256                 writer.flush();
257             } catch (IOException e) {
258                 if (!error) throw e;
259             } finally {
260                 try {
261                     writer.close();
262                 } catch (IOException e) {
263                     if (!error) throw e;
264                 }
265             }
266         }
267     }
268 
269     /**
270      * Write the entire contents of the supplied string to the given stream. This method always flushes and closes the stream when
271      * finished.
272      * 
273      * @param input the content to write to the stream; may be null
274      * @param stream the stream to which the content is to be written
275      * @throws IOException
276      * @throws IllegalArgumentException if the stream is null
277      */
278     public static void write( InputStream input,
279                               OutputStream stream ) throws IOException {
280         CheckArg.isNotNull(stream, "destination stream");
281         boolean error = false;
282         try {
283             if (input != null) {
284                 byte[] buffer = new byte[1024];
285                 try {
286                     int numRead = 0;
287                     while ((numRead = input.read(buffer)) > -1) {
288                         stream.write(buffer, 0, numRead);
289                     }
290                 } finally {
291                     input.close();
292                 }
293             }
294         } catch (IOException e) {
295             error = true; // this error should be thrown, even if there is an error flushing/closing stream
296             throw e;
297         } catch (RuntimeException e) {
298             error = true; // this error should be thrown, even if there is an error flushing/closing stream
299             throw e;
300         } finally {
301             try {
302                 stream.flush();
303             } catch (IOException e) {
304                 if (!error) throw e;
305             } finally {
306                 try {
307                     stream.close();
308                 } catch (IOException e) {
309                     if (!error) throw e;
310                 }
311             }
312         }
313     }
314 
315     /**
316      * Write the entire contents of the supplied string to the given writer. This method always flushes and closes the writer when
317      * finished.
318      * 
319      * @param input the content to write to the writer; may be null
320      * @param writer the writer to which the content is to be written
321      * @throws IOException
322      * @throws IllegalArgumentException if the writer is null
323      */
324     public static void write( Reader input,
325                               Writer writer ) throws IOException {
326         CheckArg.isNotNull(writer, "destination writer");
327         boolean error = false;
328         try {
329             if (input != null) {
330                 char[] buffer = new char[1024];
331                 try {
332                     int numRead = 0;
333                     while ((numRead = input.read(buffer)) > -1) {
334                         writer.write(buffer, 0, numRead);
335                     }
336                 } finally {
337                     input.close();
338                 }
339             }
340         } catch (IOException e) {
341             error = true; // this error should be thrown, even if there is an error flushing/closing writer
342             throw e;
343         } catch (RuntimeException e) {
344             error = true; // this error should be thrown, even if there is an error flushing/closing writer
345             throw e;
346         } finally {
347             try {
348                 writer.flush();
349             } catch (IOException e) {
350                 if (!error) throw e;
351             } finally {
352                 try {
353                     writer.close();
354                 } catch (IOException e) {
355                     if (!error) throw e;
356                 }
357             }
358         }
359     }
360 
361     /**
362      * Write the entire contents of the supplied string to the given stream. This method always flushes and closes the stream when
363      * finished.
364      * 
365      * @param input1 the first stream
366      * @param input2 the second stream
367      * @return true if the streams contain the same content, or false otherwise
368      * @throws IOException
369      * @throws IllegalArgumentException if the stream is null
370      */
371     public static boolean isSame( InputStream input1,
372                                   InputStream input2 ) throws IOException {
373         CheckArg.isNotNull(input1, "input1");
374         CheckArg.isNotNull(input2, "input2");
375         boolean error = false;
376         try {
377             byte[] buffer1 = new byte[1024];
378             byte[] buffer2 = new byte[1024];
379             try {
380                 int numRead1 = 0;
381                 int numRead2 = 0;
382                 while (true) {
383                     numRead1 = input1.read(buffer1);
384                     numRead2 = input2.read(buffer2);
385                     if (numRead1 > -1) {
386                         if (numRead2 != numRead1) return false;
387                         // Otherwise same number of bytes read
388                         if (!Arrays.equals(buffer1, buffer2)) return false;
389                         // Otherwise same bytes read, so continue ...
390                     } else {
391                         // Nothing more in stream 1 ...
392                         return numRead2 < 0;
393                     }
394                 }
395             } finally {
396                 input1.close();
397             }
398         } catch (IOException e) {
399             error = true; // this error should be thrown, even if there is an error closing stream 2
400             throw e;
401         } catch (RuntimeException e) {
402             error = true; // this error should be thrown, even if there is an error closing stream 2
403             throw e;
404         } finally {
405             try {
406                 input2.close();
407             } catch (IOException e) {
408                 if (!error) throw e;
409             }
410         }
411     }
412 
413     /**
414      * Get the {@link InputStream input stream} to the resource given by the supplied path. If a class loader is supplied, the
415      * method attempts to resolve the resource using the {@link ClassLoader#getResourceAsStream(String)} method; if the result is
416      * non-null, it is returned. Otherwise, if a class is supplied, this method attempts to resolve the resource using the
417      * {@link Class#getResourceAsStream(String)} method; if the result is non-null, it is returned. Otherwise, this method then
418      * uses the Class' ClassLoader to load the resource; if non-null, it is returned . Otherwise, this method looks for an
419      * existing and readable {@link File file} at the path; if found, a buffered stream to that file is returned. Otherwise, this
420      * method attempts to parse the resource path into a valid {@link URL}; if this succeeds, the method attempts to open a stream
421      * to that URL. If all of these fail, this method returns null.
422      * 
423      * @param resourcePath the logical path to the classpath, file, or URL resource
424      * @param clazz the class that should be used to load the resource as a stream; may be null
425      * @param classLoader the classloader that should be used to load the resource as a stream; may be null
426      * @return an input stream to the resource; or null if the resource could not be found
427      * @throws IllegalArgumentException if the resource path is null or empty
428      */
429     public static InputStream getResourceAsStream( String resourcePath,
430                                                    ClassLoader classLoader,
431                                                    Class<?> clazz ) {
432         CheckArg.isNotEmpty(resourcePath, "resourcePath");
433         InputStream result = null;
434         if (classLoader != null) {
435             // Try using the class loader first ...
436             result = classLoader.getResourceAsStream(resourcePath);
437         }
438         if (result == null && clazz != null) {
439             // Not yet found, so try the class ...
440             result = clazz.getResourceAsStream(resourcePath);
441             if (result == null) {
442                 // Not yet found, so try the class's class loader ...
443                 result = clazz.getClassLoader().getResourceAsStream(resourcePath);
444             }
445         }
446         if (result == null) {
447             // Still not found, so see if this is an existing File ...
448             try {
449                 File file = new File(resourcePath);
450                 if (file.exists() && file.canRead()) {
451                     return new BufferedInputStream(new FileInputStream(file));
452                 }
453             } catch (FileNotFoundException e) {
454                 // just continue ...
455             }
456         }
457         if (result == null) {
458             // Still not found, so try to construct a URL out of it ...
459             try {
460                 URL url = new URL(resourcePath);
461                 return url.openStream();
462             } catch (MalformedURLException e) {
463                 // just continue ...
464             } catch (IOException err) {
465                 // just continue ...
466             }
467         }
468         // Couldn't find it anywhere ...
469         return result;
470     }
471 
472     private IoUtil() {
473         // Prevent construction
474     }
475 }