View Javadoc

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