View Javadoc

1   /*
2    * $Id: FileUtils.java 11343 2008-03-13 10:58:26Z tcarlson $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSource, Inc.  All rights reserved.  http://www.mulesource.com
5    *
6    * The software in this package is published under the terms of the CPAL v1.0
7    * license, a copy of which has been included with this distribution in the
8    * LICENSE.txt file.
9    */
10  
11  package org.mule.util;
12  
13  import org.mule.MuleServer;
14  import org.mule.api.MuleRuntimeException;
15  import org.mule.config.i18n.MessageFactory;
16  
17  import java.io.BufferedOutputStream;
18  import java.io.BufferedWriter;
19  import java.io.File;
20  import java.io.FileInputStream;
21  import java.io.FileOutputStream;
22  import java.io.FileWriter;
23  import java.io.IOException;
24  import java.io.InputStream;
25  import java.io.OutputStream;
26  import java.io.UnsupportedEncodingException;
27  import java.net.JarURLConnection;
28  import java.net.URI;
29  import java.net.URL;
30  import java.net.URLConnection;
31  import java.net.URLDecoder;
32  import java.nio.channels.FileChannel;
33  import java.util.Enumeration;
34  import java.util.jar.JarEntry;
35  import java.util.jar.JarFile;
36  import java.util.zip.ZipEntry;
37  import java.util.zip.ZipFile;
38  
39  import org.apache.commons.lang.StringUtils;
40  import org.apache.commons.logging.Log;
41  import org.apache.commons.logging.LogFactory;
42  
43  /**
44   * <code>FileUtils</code> contains useful methods for dealing with files &
45   * directories.
46   */
47  // @ThreadSafe
48  public class FileUtils extends org.apache.commons.io.FileUtils
49  {
50      private static final Log logger = LogFactory.getLog(FileUtils.class);
51      public static final String DEFAULT_ENCODING = "UTF-8";
52      
53      public static synchronized void copyStreamToFile(InputStream input, File destination) throws IOException
54      {
55          if (destination.exists() && !destination.canWrite())
56          {
57              throw new IOException("Destination file does not exist or is not writeable");
58          }
59  
60          try
61          {
62              FileOutputStream output = new FileOutputStream(destination);
63              try
64              {
65                  IOUtils.copy(input, output);
66              }
67              finally
68              {
69                  IOUtils.closeQuietly(output);
70              }
71          }
72          finally
73          {
74              IOUtils.closeQuietly(input);
75          }
76      }
77  
78      // TODO Document me!
79      public static File createFile(String filename) throws IOException
80      {
81          File file = FileUtils.newFile(filename);
82          if (!file.canWrite())
83          {
84              String dirName = file.getPath();
85              int i = dirName.lastIndexOf(File.separator);
86              if (i > -1)
87              {
88                  dirName = dirName.substring(0, i);
89                  File dir = FileUtils.newFile(dirName);
90                  dir.mkdirs();
91              }
92              file.createNewFile();
93          }
94          return file;
95      }
96  
97      // TODO Document me!
98      public static String prepareWinFilename(String filename)
99      {
100         filename = filename.replaceAll("<", "(");
101         filename = filename.replaceAll(">", ")");
102         filename = filename.replaceAll("[/\\*?|:;\\]\\[\"]", "-");
103         return filename;
104     }
105 
106     // TODO Document me!
107     public static File openDirectory(String directory) throws IOException
108     {
109         File dir = FileUtils.newFile(directory);
110         if (!dir.exists())
111         {
112             dir.mkdirs();
113         }
114         if (!dir.isDirectory() || !dir.canRead())
115         {
116             throw new IOException("Path: " + directory + " exists but isn't a directory");
117         }
118         return dir;
119     }
120 
121     /**
122      * Reads the incoming String into a file at at the given destination.
123      *
124      * @param filename name and path of the file to create
125      * @param data     the contents of the file
126      * @return the new file.
127      * @throws IOException If the creating or writing to the file stream fails
128      */
129     public static File stringToFile(String filename, String data) throws IOException
130     {
131         return stringToFile(filename, data, false);
132     }
133 
134     // TODO Document me!
135     public static synchronized File stringToFile(String filename, String data, boolean append)
136             throws IOException
137     {
138         return stringToFile(filename, data, append, false);
139     }
140 
141     // TODO Document me!
142     public static synchronized File stringToFile(String filename, String data, boolean append, boolean newLine)
143             throws IOException
144     {
145         File f = createFile(filename);
146         BufferedWriter writer = null;
147         try
148         {
149             writer = new BufferedWriter(new FileWriter(f, append));
150             writer.write(data);
151             if (newLine)
152             {
153                 writer.newLine();
154             }
155         }
156         finally
157         {
158             if (writer != null)
159             {
160                 writer.close();
161             }
162         }
163         return f;
164     }
165 
166     // TODO Document me!
167     public static String getResourcePath(String resourceName, Class callingClass) throws IOException
168     {
169         return getResourcePath(resourceName, callingClass, DEFAULT_ENCODING);
170     }
171 
172     // TODO Document me!
173     public static String getResourcePath(String resourceName, Class callingClass, String encoding)
174             throws IOException
175     {
176         if (resourceName == null)
177         {
178             // no name
179             return null;
180         }
181 
182         URL url = IOUtils.getResourceAsUrl(resourceName, callingClass);
183         if (url == null)
184         {
185             // not found
186             return null;
187         }
188         return normalizeFilePath(url, encoding);
189     }
190 
191     /**
192      * Remove from uri to file prefix file:/
193      * Add if need file separator to begin
194      *
195      * @param url      file uri to resource
196      * @param encoding - Java encoding names
197      * @return normalized file path
198      * @throws UnsupportedEncodingException if encoding is unknown
199      */
200     public static String normalizeFilePath(URL url, String encoding) throws UnsupportedEncodingException
201     {
202         String resource = URLDecoder.decode(url.toExternalForm(), encoding);
203         if (resource != null)
204         {
205             if (resource.startsWith("file:/"))
206             {
207                 resource = resource.substring(6);
208             }
209             if (!resource.startsWith(File.separator))
210             {
211                 resource = File.separator + resource;
212             }
213         }
214         return resource;
215     }
216 
217 
218     /**
219      * Delete a file tree recursively.
220      * @param dir dir to wipe out
221      * @return false when the first unsuccessful attempt encountered
222      */
223     public static boolean deleteTree(File dir)
224     {
225         return deleteTree(dir, null);
226     }
227 
228     /**
229      * Delete a file tree recursively. This method additionally tries to be
230      * gentle with specified top-level dirs. E.g. this is the case when a
231      * transaction manager asynchronously handles the recovery log, and the test
232      * wipes out everything, leaving the transaction manager puzzled.  
233      * @param dir dir to wipe out
234      * @param topLevelDirsToIgnore which top-level directories to ignore,
235      *        if null or empty then ignored
236      * @return false when the first unsuccessful attempt encountered
237      */
238     public static boolean deleteTree(File dir, final String[] topLevelDirsToIgnore)
239     {
240         if (dir == null || !dir.exists())
241         {
242             return true;
243         }
244         File[] files = dir.listFiles();
245         if (files != null)
246         {
247             for (int i = 0; i < files.length; i++)
248             {
249                 OUTER:
250                 if (files[i].isDirectory())
251                 {
252                     if (topLevelDirsToIgnore != null)
253                     {
254                         for (int j = 0; j < topLevelDirsToIgnore.length; j++)
255                         {
256                             String ignored = topLevelDirsToIgnore[j];
257                             if (ignored.equals(FilenameUtils.getBaseName(files[i].getName())))
258                             {
259                                 break OUTER;
260                             }
261                         }
262                     }
263                     if (!deleteTree(files[i]))
264                     {
265                         return false;
266                     }
267                 }
268                 else
269                 {
270                     if (!files[i].delete())
271                     {
272                         return false;
273                     }
274                 }
275             }
276         }
277         return dir.delete();
278     }
279 
280     /**
281      * Unzip the specified archive to the given directory
282      */
283     public static void unzip(File archive, File directory) throws IOException
284     {
285         ZipFile zip = null;
286 
287         if (directory.exists())
288         {
289             if (!directory.isDirectory())
290             {
291                 throw new IOException("Directory is not a directory: " + directory);
292             }
293         }
294         else
295         {
296             if (!directory.mkdirs())
297             {
298                 throw new IOException("Could not create directory: " + directory);
299             }
300         }
301         try
302         {
303             zip = new ZipFile(archive);
304             for (Enumeration entries = zip.entries(); entries.hasMoreElements();)
305             {
306                 ZipEntry entry = (ZipEntry) entries.nextElement();
307                 File f = FileUtils.newFile(directory, entry.getName());
308                 if (entry.isDirectory())
309                 {
310                     if (!f.mkdirs())
311                     {
312                         throw new IOException("Could not create directory: " + f);
313                     }
314                 }
315                 else
316                 {
317                     InputStream is = zip.getInputStream(entry);
318                     OutputStream os = new BufferedOutputStream(new FileOutputStream(f));
319                     IOUtils.copy(is, os);
320                     IOUtils.closeQuietly(is);
321                     IOUtils.closeQuietly(os);
322                 }
323             }
324         }
325         finally
326         {
327             if (zip != null)
328             {
329                 zip.close();
330             }
331         }
332     }
333 
334     /**
335      * Workaround for JDK bug <a href="http://bugs.sun.com/bugdatabase/view_bug.do;:YfiG?bug_id=4117557">
336      * 4117557</a>. More in-context information at
337      * <a href="http://mule.mulesource.org/jira/browse/MULE-1112">MULE-1112</a>
338      * <p/>
339      * Factory methods correspond to constructors of the <code>java.io.File class</code>.
340      * No physical file created in this method.
341      *
342      * @see File
343      */
344     public static File newFile(String pathName)
345     {
346         try
347         {
348             return new File(pathName).getCanonicalFile();
349         }
350         catch (IOException e)
351         {
352             throw new MuleRuntimeException(
353                     MessageFactory.createStaticMessage("Unable to create a canonical file for " + pathName),
354                     e);
355         }
356     }
357 
358     /**
359      * Workaround for JDK bug <a href="http://bugs.sun.com/bugdatabase/view_bug.do;:YfiG?bug_id=4117557">
360      * 4117557</a>. More in-context information at
361      * <a href="http://mule.mulesource.org/jira/browse/MULE-1112">MULE-1112</a>
362      * <p/>
363      * Factory methods correspond to constructors of the <code>java.io.File class</code>.
364      * No physical file created in this method.
365      *
366      * @see File
367      */
368     public static File newFile(URI uri)
369     {
370         try
371         {
372             return new File(uri).getCanonicalFile();
373         }
374         catch (IOException e)
375         {
376             throw new MuleRuntimeException(
377                     MessageFactory.createStaticMessage("Unable to create a canonical file for " + uri),
378                     e);
379         }
380     }
381 
382     /**
383      * Workaround for JDK bug <a href="http://bugs.sun.com/bugdatabase/view_bug.do;:YfiG?bug_id=4117557">
384      * 4117557</a>. More in-context information at
385      * <a href="http://mule.mulesource.org/jira/browse/MULE-1112">MULE-1112</a>
386      * <p/>
387      * Factory methods correspond to constructors of the <code>java.io.File class</code>.
388      * No physical file created in this method.
389      *
390      * @see File
391      */
392     public static File newFile(File parent, String child)
393     {
394         try
395         {
396             return new File(parent, child).getCanonicalFile();
397         }
398         catch (IOException e)
399         {
400             throw new MuleRuntimeException(
401                     MessageFactory.createStaticMessage("Unable to create a canonical file for parent: "
402                             + parent + " and child: " + child),
403                     e);
404         }
405     }
406 
407     /**
408      * Workaround for JDK bug <a href="http://bugs.sun.com/bugdatabase/view_bug.do;:YfiG?bug_id=4117557">
409      * 4117557</a>. More in-context information at
410      * <a href="http://mule.mulesource.org/jira/browse/MULE-1112">MULE-1112</a>
411      * <p/>
412      * Factory methods correspond to constructors of the <code>java.io.File class</code>.
413      * No physical file created in this method.
414      *
415      * @see File
416      */
417     public static File newFile(String parent, String child)
418     {
419         try
420         {
421             return new File(parent, child).getCanonicalFile();
422         }
423         catch (IOException e)
424         {
425             throw new MuleRuntimeException(
426                     MessageFactory.createStaticMessage("Unable to create a canonical file for parent: "
427                             + parent + " and child: " + child),
428                     e);
429         }
430     }
431 
432     /**
433      * Extract the specified resource to the given directory for
434      * remain all directory struct
435      *
436      * @param resourceName        - full resource name
437      * @param callingClass        - classloader for this class is used
438      * @param outputDir           - extract to this directory
439      * @param keepParentDirectory true -  full structure of directories is kept; false - file - removed all directories, directory - started from resource point
440      * @throws IOException if any errors
441      */
442     public static void extractResources(String resourceName, Class callingClass, File outputDir, boolean keepParentDirectory) throws IOException
443     {
444         URL url = callingClass.getClassLoader().getResource(resourceName);
445         URLConnection connection = url.openConnection();
446         if (connection instanceof JarURLConnection)
447         {
448             extractJarResources((JarURLConnection) connection, outputDir, keepParentDirectory);
449         }
450         else
451         {
452             extractFileResources(normalizeFilePath(url,
453                                                    MuleServer.getMuleContext().getConfiguration().getDefaultEncoding()),
454                                                    outputDir, resourceName, keepParentDirectory);
455         }
456     }
457 
458     /**
459      * Extract resources contain in file
460      *
461      * @param path                - path to file
462      * @param outputDir           Directory for unpack recources
463      * @param resourceName
464      * @param keepParentDirectory true -  full structure of directories is kept; false - file - removed all directories, directory - started from resource point
465      * @throws IOException if any error
466      */
467     private static void extractFileResources(String path, File outputDir, String resourceName, boolean keepParentDirectory) throws IOException
468     {
469         File file = FileUtils.newFile(path);
470         if (!file.exists())
471         {
472             throw new IOException("The resource by path " + path + " ");
473         }
474         if (file.isDirectory())
475         {
476             if (keepParentDirectory)
477             {
478                 outputDir = FileUtils.newFile(outputDir.getPath() + File.separator + resourceName);
479                 if (!outputDir.exists())
480                 {
481                     outputDir.mkdirs();
482                 }
483             }
484             else
485             {
486                 outputDir = FileUtils.newFile(outputDir.getPath());
487             }
488             copyDirectory(file, outputDir);
489         }
490         else
491         {
492 
493             if (keepParentDirectory)
494             {
495                 outputDir = FileUtils.newFile(outputDir.getPath() + File.separator + resourceName);
496             }
497             else
498             {
499                 outputDir = FileUtils.newFile(outputDir.getPath() + File.separator + file.getName());
500             }
501             copyFile(file, outputDir);
502         }
503     }
504 
505     /**
506      * Extract recources contain if jar (have to in classpath)
507      *
508      * @param connection          JarURLConnection to jar library
509      * @param outputDir           Directory for unpack recources
510      * @param keepParentDirectory true -  full structure of directories is kept; false - file - removed all directories, directory - started from resource point
511      * @throws IOException if any error
512      */
513     private static void extractJarResources(JarURLConnection connection, File outputDir, boolean keepParentDirectory) throws IOException
514     {
515         JarFile jarFile = connection.getJarFile();
516         JarEntry jarResource = connection.getJarEntry();
517         Enumeration entries = jarFile.entries();
518         InputStream inputStream = null;
519         OutputStream outputStream = null;
520         int jarResourceNameLenght = jarResource.getName().length();
521         for (; entries.hasMoreElements();)
522         {
523             JarEntry entry = (JarEntry) entries.nextElement();
524             if (entry.getName().startsWith(jarResource.getName()))
525             {
526 
527                 String path = outputDir.getPath() + File.separator + entry.getName();
528 
529                 //remove directory struct for file and first dir for directory
530                 if (!keepParentDirectory)
531                 {
532                     if (entry.isDirectory())
533                     {
534                         if (entry.getName().equals(jarResource.getName()))
535                         {
536                             continue;
537                         }
538                         path = outputDir.getPath() + File.separator + entry.getName().substring(jarResourceNameLenght, entry.getName().length());
539                     }
540                     else
541                     {
542                         if (entry.getName().length() > jarResourceNameLenght)
543                         {
544                             path = outputDir.getPath() + File.separator + entry.getName().substring(jarResourceNameLenght, entry.getName().length());
545                         }
546                         else
547                         {
548                             path = outputDir.getPath() + File.separator + entry.getName().substring(entry.getName().lastIndexOf("/"), entry.getName().length());
549                         }
550                     }
551                 }
552 
553                 File file = FileUtils.newFile(path);
554                 if (!file.getParentFile().exists())
555                 {
556                     if (!file.getParentFile().mkdirs())
557                     {
558                         throw new IOException("Could not create directory: " + file.getParentFile());
559                     }
560                 }
561                 if (entry.isDirectory())
562                 {
563                     if (!file.exists() && !file.mkdirs())
564                     {
565                         throw new IOException("Could not create directory: " + file);
566                     }
567 
568                 }
569                 else
570                 {
571                     try
572                     {
573                         inputStream = jarFile.getInputStream(entry);
574                         outputStream = new BufferedOutputStream(new FileOutputStream(file));
575                         IOUtils.copy(inputStream, outputStream);
576                     }
577                     finally
578                     {
579                         IOUtils.closeQuietly(inputStream);
580                         IOUtils.closeQuietly(outputStream);
581                     }
582                 }
583 
584             }
585         }
586     }
587 
588     public static boolean renameFileHard(String srcFilePath, String destFilePath)
589     {
590         if (StringUtils.isNotBlank(srcFilePath) && StringUtils.isNotBlank(destFilePath))
591         {
592             return renameFileHard(new File(srcFilePath), new File(destFilePath));
593         }
594         else
595         {
596             return false;
597         }
598     }
599     
600     public static boolean renameFileHard(File srcFile, File destFile)
601     {
602         boolean isRenamed = false;
603         if (srcFile != null && destFile != null)
604         {
605             logger.debug("Moving file " + srcFile.getAbsolutePath() + " to " + destFile.getAbsolutePath());
606             if (!destFile.exists())
607             {
608                 try
609                 {
610                     if (srcFile.isFile())
611                     {
612                         logger.debug("Trying to rename file");
613                         FileInputStream in = null;
614                         FileOutputStream out = null;
615                         try
616                         {
617                             in = new FileInputStream(srcFile);
618                             out = new FileOutputStream(destFile);
619                             out.getChannel().transferFrom(in.getChannel(), 0, srcFile.length());
620                             isRenamed = true;
621                         }
622                         catch (Exception e)
623                         {
624                             logger.debug(e);
625                         }
626                         finally
627                         {
628                             if (in != null)
629                             {
630                                 try
631                                 {
632                                     in.close();
633                                 }
634                                 catch (Exception inNotClosed)
635                                 {
636                                     logger.debug(inNotClosed);
637                                 }
638                             }
639                             if (out != null)
640                             {
641                                 try
642                                 {
643                                     out.close();
644                                 }
645                                 catch (Exception outNotClosed)
646                                 {
647                                     logger.debug(outNotClosed);
648                                 }
649                             }
650                         }
651                         logger.debug("File renamed: " + isRenamed);
652                         if (isRenamed)
653                         {
654                             srcFile.delete();
655                         }
656                         else
657                         {
658                             destFile.delete();
659                         }
660                     }
661                     else
662                     {
663                         logger.debug(srcFile.getAbsolutePath() + " is not a valid file.");
664                     }
665                 }
666                 catch (Exception e)
667                 {
668                     logger.debug("Error renaming file from " + srcFile.getAbsolutePath() + " to " + destFile.getAbsolutePath());
669                 }
670             }
671             else
672             {
673                 logger.debug("Error renaming file " + srcFile.getAbsolutePath() + ". Destination file " + destFile.getAbsolutePath() + " already exists.");
674             }
675         }
676         return isRenamed;
677     }
678 
679     public static boolean renameFile(String srcFilePath, String destFilePath)
680     {
681         if (StringUtils.isNotBlank(srcFilePath) && StringUtils.isNotBlank(destFilePath))
682         {
683             return renameFile(new File(srcFilePath), new File(destFilePath));
684         }
685         else
686         {
687             return false;
688         }
689     }
690     
691     public static boolean renameFile(File srcFile, File destFile)
692     {
693         boolean isRenamed = false;
694         if (srcFile != null && destFile != null)
695         {
696             logger.debug("Moving file " + srcFile.getAbsolutePath() + " to " + destFile.getAbsolutePath());
697             if (!destFile.exists())
698             {
699                 try
700                 {
701                     if (srcFile.isFile())
702                     {
703                         logger.debug("Trying to rename file");
704                         isRenamed = srcFile.renameTo(destFile);
705                         if (!isRenamed && srcFile.exists())
706                         {
707                             logger.debug("Trying hard copy, assuming partition crossing ...");
708                             isRenamed = renameFileHard(srcFile, destFile);
709                         }
710                         logger.debug("File renamed: " + isRenamed);
711                     }
712                     else
713                     {
714                         logger.debug(srcFile.getAbsolutePath() + " is not a valid file");
715                     }
716                 }
717                 catch (Exception e)
718                 {
719                     logger.debug("Error moving file from " + srcFile.getAbsolutePath() + " to " + destFile.getAbsolutePath(), e);
720                 }
721             }
722             else
723             {
724                 logger.debug("Error renaming file " + srcFile.getAbsolutePath() + ". Destination file " + destFile.getAbsolutePath() + " already exists.");
725             }
726         }
727         else
728         {
729             logger.debug("Error renaming file. Source or destination file is null.");
730         }
731     
732         return isRenamed;
733     }
734     
735     /** Try to move a file by renaming with backup attempt by copying/deleting via NIO */
736     public static boolean moveFile(File sourceFile, File destinationFile)
737     {
738         // try fast file-system-level move/rename first
739         boolean success = sourceFile.renameTo(destinationFile);
740 
741         if (!success)
742         {
743             // try again using NIO copy
744             FileInputStream fis = null;
745             FileOutputStream fos = null;
746             try
747             {
748                 fis = new FileInputStream(sourceFile);
749                 fos = new FileOutputStream(destinationFile);
750                 FileChannel srcChannel = fis.getChannel();
751                 FileChannel dstChannel = fos.getChannel();
752                 dstChannel.transferFrom(srcChannel, 0, srcChannel.size());
753                 srcChannel.close();
754                 dstChannel.close();
755                 success = sourceFile.delete();
756             }
757             catch (IOException ioex)
758             {
759                 // grr!
760                 success = false;
761             }
762             finally
763             {
764                 IOUtils.closeQuietly(fis);
765                 IOUtils.closeQuietly(fos);
766             }
767         }
768 
769         return success;
770     }
771 
772 }