1   package com.atlassian.user.util;
2   
3   import org.apache.log4j.Logger;
4   
5   import java.io.*;
6   
7   /*
8    * Atlassian Source Code Template.
9    * User: anton
10   * Date: 15/08/2003
11   * Time: 11:38:41
12   * CVS Revision: $Revision: 2774 $
13   * Last CVS Commit: $Date: 2006-01-12 22:12:04 -0600 (Thu, 12 Jan 2006) $
14   * Author of last CVS Commit: $Author: nfaiz $
15   */
16  
17  public class FileUtils
18  {
19      private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
20      private static final Logger log = Logger.getLogger(FileUtils.class);
21  
22      /**
23       * Copy file from source to destination. The directories up to <code>destination</code> will be
24       * created if they don't already exist. <code>destination</code> will be overwritten if it
25       * already exists.
26       *
27       * @param source      An existing non-directory <code>File</code> to copy bytes from.
28       * @param destination A non-directory <code>File</code> to write bytes to (possibly
29       *                    overwriting).
30       * @throws java.io.IOException if <code>source</code> does not exist, <code>destination</code> cannot be
31       *                             written to, or an IO error occurs during copying.
32       */
33      public static void copyFile(final File source, final File destination)
34              throws IOException
35      {
36          //check source exists
37          if (!source.exists())
38          {
39              final String message = "File " + source + " does not exist";
40              throw new IOException(message);
41          }
42  
43          //does destinations directory exist ?
44          if (destination.getParentFile() != null &&
45                  !destination.getParentFile().exists())
46          {
47              destination.getParentFile().mkdirs();
48          }
49  
50          //make sure we can write to destination
51          if (destination.exists() && !destination.canWrite())
52          {
53              final String message = "Unable to open file " +
54                      destination + " for writing.";
55              throw new IOException(message);
56          }
57  
58          final FileInputStream input = new FileInputStream(source);
59          final FileOutputStream output = new FileOutputStream(destination);
60          copy(input, output);
61          shutdownStream(input);
62          shutdownStream(output);
63  
64          if (source.length() != destination.length())
65          {
66              final String message = "Failed to copy full contents from " + source +
67                      " to " + destination;
68              throw new IOException(message);
69          }
70      }
71  
72      /**
73       * Copy bytes from an <code>InputStream</code> to an <code>OutputStream</code>.
74       *
75       * @param input  the <code>InputStream</code> to read from
76       * @param output the <code>OutputStream</code> to write to
77       * @return the number of bytes copied
78       * @throws IOException In case of an I/O problem
79       */
80      public static int copy(final InputStream input, final OutputStream output)
81              throws IOException
82      {
83          return copy(input, output, DEFAULT_BUFFER_SIZE);
84      }
85  
86      /**
87       * Copy bytes from an <code>InputStream</code> to an <code>OutputStream</code>.
88       *
89       * @param input      the <code>InputStream</code> to read from
90       * @param output     the <code>OutputStream</code> to write to
91       * @param bufferSize Size of internal buffer to use.
92       * @return the number of bytes copied
93       * @throws IOException In case of an I/O problem
94       */
95      public static int copy(final InputStream input,
96                             final OutputStream output,
97                             final int bufferSize)
98              throws IOException
99      {
100         final byte[] buffer = new byte[bufferSize];
101         int count = 0;
102         int n = 0;
103         while (-1 != (n = input.read(buffer)))
104         {
105             output.write(buffer, 0, n);
106             count += n;
107         }
108         return count;
109     }
110 
111     /**
112      * Unconditionally close an <code>OutputStream</code>.
113      * Equivalent to {@link OutputStream#close()}, except any exceptions will be ignored.
114      *
115      * @param output A (possibly null) OutputStream
116      */
117     public static void shutdownStream(final OutputStream output)
118     {
119         if (output == null)
120         {
121             return;
122         }
123 
124         try
125         {
126             output.close();
127         }
128         catch (final IOException ioe)
129         {
130         }
131     }
132 
133     /**
134      * Unconditionally close an <code>InputStream</code>.
135      * Equivalent to {@link InputStream#close()}, except any exceptions will be ignored.
136      *
137      * @param input A (possibly null) InputStream
138      */
139     public static void shutdownStream(final InputStream input)
140     {
141         if (input == null)
142         {
143             return;
144         }
145 
146         try
147         {
148             input.close();
149         }
150         catch (final IOException ioe)
151         {
152         }
153     }
154 
155 
156     /**
157      * safely performs a recursive delete on a directory
158      *
159      * @param dir
160      * @return
161      */
162     public static boolean deleteDir(File dir)
163     {
164         if (dir == null)
165         {
166             return false;
167         }
168 
169         // to see if this directory is actually a symbolic link to a directory,
170         // we want to get its canonical path - that is, we follow the link to
171         // the file it's actually linked to
172         File candir;
173         try
174         {
175             candir = dir.getCanonicalFile();
176         }
177         catch (IOException e)
178         {
179             return false;
180         }
181 
182         // a symbolic link has a different canonical path than its actual path,
183         // unless it's a link to itself
184         if (!candir.equals(dir.getAbsoluteFile()))
185         {
186             // this file is a symbolic link, and there's no reason for us to
187             // follow it, because then we might be deleting something outside of
188             // the directory we were told to delete
189             return false;
190         }
191 
192         // now we go through all of the files and subdirectories in the
193         // directory and delete them one by one
194         File[] files = candir.listFiles();
195         if (files != null)
196         {
197             for (int i = 0; i < files.length; i++)
198             {
199                 File file = files[i];
200 
201                 // in case this directory is actually a symbolic link, or it's
202                 // empty, we want to try to delete the link before we try
203                 // anything
204                 boolean deleted = !file.delete();
205                 if (deleted)
206                 {
207                     // deleting the file failed, so maybe it's a non-empty
208                     // directory
209                     if (file.isDirectory()) deleteDir(file);
210 
211                     // otherwise, there's nothing else we can do
212                 }
213             }
214         }
215 
216         // now that we tried to clear the directory out, we can try to delete it
217         // again
218         return dir.delete();
219     }
220 
221     public static void deleteFilesBeginningWith(String directory, String prefix)
222     {
223         File dir = new File(directory);
224 
225         if (!dir.isDirectory())
226             throw new IllegalArgumentException("directory arg. is not a dir. [" + directory + "]");
227 
228         File[] files = dir.listFiles();
229 
230         for (int i = 0; i < files.length; i++)
231         {
232             File file = files[i];
233             if (file.getName().startsWith(prefix))
234             {
235                 if (!file.delete())
236                     throw new RuntimeException("Could not delete " + file.getName());
237             }
238         }
239     }
240 
241     /**
242      * creates a temp file with a given filename from an input stream in the classpath or a file in the filesystem
243      *
244      * @param fileName
245      * @return
246      * @throws IOException
247      */
248     public static File copyIntoTemporaryFile(String source, String fileName) throws IOException, Exception
249     {
250         File temp = File.createTempFile(fileName, null);
251         temp.deleteOnExit();
252 
253         // Write content to the tempfile
254         BufferedWriter out = new BufferedWriter(new FileWriter(temp));
255         out.write(source);
256         out.close();
257 
258         return temp;
259     }
260 
261     public static String getInputStreamTextContent(InputStream is)
262     {
263         if (is == null)
264         {
265             return null;
266         }
267 
268         String result = null;
269 
270         try
271         {
272             ByteArrayOutputStream baos = new ByteArrayOutputStream(is.available());
273 
274             pump(is, baos);
275 
276             result = new String(baos.toByteArray());
277 
278             is.close();
279         }
280         catch (IOException e)
281         {
282             log.error("IOException reading stream: " + e, e);
283         }
284 
285         return result;
286     }
287 
288 
289     private static void pump(InputStream is, OutputStream os) throws IOException
290     {
291         byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
292         int lengthRead;
293 
294         while ((lengthRead = is.read(buffer)) >= 0)
295         {
296             os.write(buffer, 0, lengthRead);
297         }
298     }
299 }