View Javadoc

1   /*
2   Copyright (C) 2000 - 2007 Grid Systems, S.A.
3   
4   This program is free software; you can redistribute it and/or modify
5   it under the terms of the GNU General Public License, version 2, as
6   published by the Free Software Foundation.
7   
8   This program is distributed in the hope that it will be useful,
9   but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  GNU General Public License for more details.
12  
13  You should have received a copy of the GNU General Public License
14  along with this program; if not, write to the Free Software
15  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16  */
17  package com.gridsystems.utils;
18  
19  import java.io.BufferedInputStream;
20  import java.io.BufferedOutputStream;
21  import java.io.BufferedReader;
22  import java.io.File;
23  import java.io.FileFilter;
24  import java.io.FileInputStream;
25  import java.io.FileOutputStream;
26  import java.io.FileReader;
27  import java.io.IOException;
28  import java.io.InputStream;
29  import java.io.OutputStream;
30  import java.lang.reflect.Method;
31  import java.util.ArrayList;
32  import java.util.Arrays;
33  import java.util.Enumeration;
34  import java.util.List;
35  import java.util.Properties;
36  import java.util.zip.ZipEntry;
37  import java.util.zip.ZipFile;
38  import java.util.zip.ZipInputStream;
39  import java.util.zip.ZipOutputStream;
40  
41  import org.apache.commons.logging.Log;
42  import org.apache.commons.logging.LogFactory;
43  
44  /**
45   * General File Utilities.
46   *
47   * @author Xmas
48   * @version 1.0
49   */
50  public final class FileUtils {
51  
52    /**
53     * Oner KB in bytes.
54     */
55    public static final int ONE_KB_IN_BYTES = 1024;
56  
57    /**
58     * Four KB in bytes.
59     */
60    public static final int FOUR_KB_IN_BYTES = 4096;
61  
62    /**
63     * Four KB in bytes.
64     */
65    public static final int THIRTY_TWO_KB_IN_BYTES = 32 * 1024;
66  
67    /** Logger for this class. */
68    private static Log log = LogFactory.getLog(FileUtils.class);
69  
70    /** Hidden constructor. */
71    private FileUtils() {
72    }
73  
74    /**
75     * Closes any object having a close() method without exceptions.
76     * @param obj  The object to close
77     */
78    public static void close(Object obj) {
79      try {
80        Method m = obj.getClass().getMethod("close");
81        m.invoke(obj);
82      } catch (Exception ignore) { }
83    }
84  
85    /**
86     * Stores properties to a file.
87     *
88     * @param f      The destination file
89     * @param props  The source properties
90     * @param header The header line text
91     * @throws IOException If an error occurs
92     */
93    public static synchronized void savePropertiesToFile(
94      File f, Properties props, String header) throws IOException {
95  
96      checkFileExists(f);
97  
98      if (props == null) {
99        props = new Properties();
100     }
101 
102     FileOutputStream fos = null;
103     try {
104       fos = new FileOutputStream(f);
105       props.store(fos, header);
106     } catch (Exception e) {
107       log.error("Error writing properties into file:" + f.getAbsolutePath(), e);
108       throw new IOException(I18n.translate("error.write.properties",
109         f.getAbsolutePath(), e.getMessage()));
110     } finally {
111       close(fos);
112     }
113   }
114 
115   /**
116    * Read properties from a file.
117    * @param f The file to read
118    * @return The read properties
119    * @throws IOException If an error occurs
120    */
121   public static Properties loadPropertiesFromFile(File f) throws IOException {
122     checkFileExists(f);
123     FileInputStream fis = null;
124     try {
125       Properties props = new Properties();
126       fis = new FileInputStream(f);
127       props.load(fis);
128       return props;
129     } catch (IOException e) {
130       log.error(
131         "Error reading properties from file:" + f.getAbsolutePath(), e);
132       throw new IOException(I18n.translate("error.read.properties",
133         new String[]{f.getAbsolutePath(), e.getMessage()}));
134     } finally {
135       close(fis);
136     }
137   }
138 
139   /**
140    * Merge 2 properties into one.
141    * @param base the first Properties object
142    * @param olds the second Properties object
143    * @return a new Properties object
144    */
145   public static Properties mergeProperties(Properties base, Properties olds) {
146     Properties merge = new Properties(olds);
147     // Read fist properties
148     if (base != null) {
149       for (Object key : base.keySet()) {
150         merge.setProperty((String)key, base.getProperty((String)key));
151       }
152     }
153     // Read second properties
154     if (olds != null) {
155       for (Object key : olds.keySet()) {
156         merge.setProperty((String)key, olds.getProperty((String)key));
157       }
158     }
159     // Return merged properties
160     return merge;
161   }
162 
163   /**
164    * Reads the content of the text file and returns it as a String.
165    * @param f the file to read from
166    * @return the contents of the file
167    * @throws IOException if error
168    */
169   public static String readText(File f) throws IOException {
170     checkFileExists(f);
171     FileInputStream fis = null;
172     try {
173       fis = new FileInputStream(f);
174       StringBuffer str = new StringBuffer();
175       int length;
176       byte[] datos = new byte[FOUR_KB_IN_BYTES];
177       while ((length = fis.read(datos)) != -1) {
178         str.append(new String(datos, 0, length));
179       }
180       return str.toString();
181     } catch (Exception e) {
182       log.error("Error reading file:" + f.getAbsolutePath(), e);
183       throw new IOException(I18n.translate("error.read.file",
184         new String[]{f.getAbsolutePath(), e.getMessage()}));
185     } finally {
186       close(fis);
187     }
188   }
189 
190   /**
191    * Reads a text file containing a string item by line, and returns an
192    * array containing those lines not beginning by a '#' character.
193    * @param f The file to read
194    * @return  The lines of the file
195    * @throws IOException if error
196    */
197   public static String[] readListFile(File f) throws IOException {
198     List<String> list = new ArrayList<String>();
199     BufferedReader reader = null;
200     try {
201       reader = new BufferedReader(new FileReader(f), FOUR_KB_IN_BYTES);
202       String line;
203       while ((line = reader.readLine()) != null) {
204         line = line.trim();
205         if (!"".equals(line) && !line.startsWith("#")) {
206           list.add(line);
207         }
208       }
209       return list.toArray(new String[list.size()]);
210     } finally {
211       close(reader);
212     }
213   }
214 
215   /**
216    * Writes text into a file.
217    * @param f the file to write to
218    * @param text the text to be written
219    * @throws IOException if error
220    */
221   public static void writeText(File f, String text) throws IOException {
222     if (f == null) {
223       throw new IOException(I18n.translate("error.null.fileparameter"));
224     }
225     if (text == null) {
226       log.warn("FileUtils::writeText() -> Text is null.");
227       text = "";
228     }
229     FileOutputStream fos = null;
230     try {
231       fos = new FileOutputStream(f);
232       fos.write(text.getBytes());
233       fos.flush();
234     } catch (IOException e) {
235       log.error("Error writing file:" + f.getAbsolutePath(), e);
236       throw new IOException(I18n.translate("error.write.file",
237         new String[]{f.getAbsolutePath(), e.getMessage()}));
238     } finally {
239       close(fos);
240     }
241   }
242 
243   /** Crea un archivo zip del contenido de la raiz de cierto directorio.
244    * @param zip Archivo donde guardar el directorio comprimido
245    * @param filetozip archivo de directorio a comprimir
246    * @param filter filtro a aplicar para la selecciona de archivos a comprimir
247    * @throws Exception if error
248    */
249   public static void zip(File zip, File filetozip, FileFilter filter)
250     throws Exception {
251     if (filetozip.isFile()) {
252       // File: directory base is parent
253       zip(zip, filetozip.getParentFile(), filetozip, filter);
254     } else {
255       // Directory: directory base is the same directory
256       zip(zip, filetozip, filetozip, filter);
257     }
258   }
259 
260   /**
261    * Crea un archivo zip del contenido de la raiz de cierto directorio.
262    * @param dst Archivo donde guardar el directorio comprimido
263    * @param base Directorio base a partir del cual apareceran las rutas en los
264    * archivos
265    * @param src archivo de directorio a comprimir
266    * @param filter filtro a aplicar para la selecciona de archivos a comprimir
267    * @throws IOException if error
268    */
269   public static void zip(File dst, File base, File src, FileFilter filter)
270     throws IOException {
271     if (dst == null || base == null || src == null) {
272       // Check null parameter
273       throw new IOException(I18n.translate("error.null.fileparameter"));
274     } else if (!base.isDirectory()) {
275       // Check if base is a directory
276       String str = base.getAbsolutePath();
277       throw new IOException(I18n.translate("error.notdirectory", str));
278     } else if (!src.exists()) {
279       // Check if exist directory to compress
280       throw new IOException(I18n.translate("error.zip.directorynull"));
281     }
282     // Calcula la ruta relativa a la base
283     String relative = "";
284     String canBase = base.getCanonicalPath();
285     String canDir;
286     if (src.isDirectory()) {
287       canDir = src.getCanonicalPath();
288     } else {
289       canDir = src.getParentFile().getCanonicalPath();
290     }
291     // Check if base is any parent of filetozip
292     if (canBase.length() > canDir.length()) {
293       throw new IOException(I18n.translate("error.notparent", canBase, canDir));
294     } else if (!canDir.substring(0, canBase.length()).equals(canBase)) {
295       throw new IOException(I18n.translate("error.notparent", canBase, canDir));
296     }
297     // Son directorios diferentes ?
298     if (canBase.length() < canDir.length()) {
299       if (!canBase.endsWith(File.separator)) {
300         canBase = canBase + File.separator;
301       }
302       if (!canDir.endsWith(File.separator)) {
303         canDir = canDir + File.separator;
304       }
305       relative = canDir.substring(canBase.length(), canDir.length());
306     }
307     // Outputs de salida
308     ZipOutputStream zos = null;
309     try {
310       zos = new ZipOutputStream(new FileOutputStream(dst));
311       internalCreateZip(zos, relative, src, filter);
312       zos.flush();
313     } catch (IOException ex) {
314       // Gestion de errores
315       log.error("FileUtils::createZip() -> Error creando zip del directorio "
316         + src.getAbsolutePath(), ex);
317       throw new IOException(I18n.translate("error.zip.create", src.getAbsolutePath()));
318     } finally {
319       close(zos);
320     }
321   }
322 
323   /**
324    * Crea un archivo zip del contenido de la raiz de cierto directorio.
325    * Devuelve una referencia al archivo zip generado
326    * @param zos ZipOutputStream
327    * @param base directorio base
328    * @param filetozip fichero para comprimir
329    * @param filter filtro
330    * @throws IOException if error
331    */
332   private static void internalCreateZip(ZipOutputStream zos, String base,
333     File filetozip, FileFilter filter) throws IOException {
334 
335     if (filetozip.isFile()) {
336       // Create a file input stream and a buffered input stream.
337       FileInputStream fis = new FileInputStream(filetozip);
338       // Create a Zip Entry and put it into the archive (no data yet).
339       ZipEntry fileEntry = new ZipEntry(base + filetozip.getName());
340       zos.putNextEntry(fileEntry);
341       // Guardamos el contenido
342       int len = 0;
343       byte[] b = new byte[ONE_KB_IN_BYTES];
344       while ((len = fis.read(b)) != -1) {
345         zos.write(b, 0, len);
346       }
347       close(fis);
348       zos.closeEntry();
349     } else {
350       // Lista de archivos a zippear
351       File[] fileList;
352       if (filter == null) {
353         fileList = filetozip.listFiles();
354       } else {
355         fileList = filetozip.listFiles(filter);
356       }
357 
358       for (int i = 0; i < fileList.length; i++) {
359         // Process each file
360         if (fileList[i].isFile()) {
361           internalCreateZip(zos, base, fileList[i], filter);
362         } else {
363           internalCreateZip(zos, base + fileList[i].getName() + File.separator,
364             fileList[i], filter);
365         }
366       } // for loop
367     }
368   }
369 
370   /**
371    * Uncompresses the specified file into the specified directory.
372    * @param dir The directory into which to unzip
373    * @param f The file to unzip
374    * @param stripPrefix Flag that controls if the path prefix is stripped from entries
375    * @throws IOException If an error occurs
376    * @author rruiz,xmas
377    */
378   public static void unzip(File dir, File f, boolean stripPrefix)
379     throws IOException {
380     unzip(dir, f, stripPrefix, null, null);
381   }
382 
383   /**
384    * Uncompresses the specified file into the specified directory.
385    *
386    * @param dir The directory into which to unzip
387    * @param f The file to unzip
388    * @param unziplistener Listenr for know status of unzipping
389    * @throws IOException If an error occurs
390    * @author rruiz,xmas
391    */
392   public static void unzip(File dir, File f, UnzipListener unziplistener)
393     throws IOException {
394     unzip(dir, f, false, unziplistener, null);
395   }
396 
397   /**
398    * Uncompresses the specified file into the specified directory filtering by filter.
399    * @param dir The directory into which to unzip
400    * @param f The file to unzip
401    * @param filter Filter files to extract (Only files, not directories)
402    * @throws IOException If an error occurs
403    * @author rruiz,xmas
404    */
405   public static void unzip(File dir, File f, FileFilter filter)
406     throws IOException {
407     unzip(dir, f, false, null, filter);
408   }
409 
410   /**
411    * Uncompresses the specified file into the specified directory.
412    * @param dir The directory into which to unzip
413    * @param f The file to unzip
414    * @param stripPrefix Flag that controls if the path prefix is stripped from entries
415    * @param unziplistener Listenr for know status of unzipping
416    * @throws IOException If an error occurs
417    */
418   public static void unzip(File dir, File f, boolean stripPrefix,
419     UnzipListener unziplistener)  throws IOException {
420     unzip(dir, f, stripPrefix, unziplistener, null);
421   }
422 
423   /**
424    * Checks that the specified file is not null, exists and it is indeed a file.
425    *
426    * @param f The file to check
427    * @throws IOException If f is null or it is not an existing file
428    */
429   private static void checkFileExists(File f) throws IOException {
430     if (f == null) {
431       throw new IOException(I18n.translate("error.null.fileparameter"));
432     } else if (!f.isFile()) {
433       throw new IOException(I18n.translate("error.filenotfound", f.getAbsolutePath()));
434     }
435   }
436 
437   /**
438    * Uncompresses the specified file into the specified directory.
439    * @param dir The directory into which to unzip
440    * @param f The file to unzip
441    * @param stripPrefix Flag that controls if the path prefix is stripped from entries
442    * @param unziplistener Listenr for know status of unzipping
443    * @param filefilter Filter files to extract (Only files, not directories)
444    * @throws IOException If an error occurs
445    */
446   public static void unzip(File dir, File f, boolean stripPrefix,
447     UnzipListener unziplistener, FileFilter filefilter) throws IOException {
448 
449     checkFileExists(f);
450     if (dir == null) {
451       // Check null parameter
452       throw new IOException(I18n.translate("error.null.fileparameter"));
453     } else if (dir.exists()) {
454       // Check if dir exist and is a directory */
455       if (!dir.isDirectory()) {
456         String str = dir.getAbsolutePath();
457         throw new IOException(I18n.translate("error.notdirectory", str));
458       }
459     } else {
460       dir.mkdirs();
461     }
462 
463     // Finds out if an entry with the same name as the source zip should be skipped
464     f = f.getCanonicalFile();
465 
466     // Start compression
467     ZipFile zf = null;
468     try {
469       zf = new ZipFile(f);
470       Enumeration<? extends ZipEntry> entries = zf.entries();
471       boolean first = true;
472       String prefix = null;
473       int pos = 0;
474        // Notify number or entries
475       if (unziplistener != null) {
476         unziplistener.setZipSize(zf.size());
477       }
478       // Process all files
479       while (entries.hasMoreElements()) {
480         ZipEntry entry = entries.nextElement();
481 
482         if (first && stripPrefix) {
483           // First entry. Determines the prefix of the rest
484           first = false;
485           prefix = entry.getName();
486           pos = prefix.length();
487         } else {
488           String path = entry.getName().replace('\\', '/');
489           if (stripPrefix) {
490             path = path.substring(pos);
491           }
492 
493           File file = new File(dir, path).getCanonicalFile();
494           if (file.equals(f)) {
495             // This prevents overwritting the zip we are reading
496             continue;
497           } else if (unziplistener != null) {
498             unziplistener.unzipingFile(path);
499           }
500 
501           if (entry.isDirectory()) {
502             file.mkdirs();
503           } else if (filefilter == null || filefilter.accept(file)) {
504             // Notify which file is processing
505             OutputStream out = null;
506             try {
507               file.getParentFile().mkdirs();
508               InputStream in = zf.getInputStream(entry);
509               out = new FileOutputStream(file);
510               copyStream(out, in);
511             } finally {
512               close(out);
513             }
514           }
515         }
516       }
517     } finally {
518       close(zf);
519     }
520   }
521 
522   /** Listener used to show information of unzipping process. */
523   public interface UnzipListener {
524     /** Notifies that file is unzipping in this moment.
525      * @param file the file
526      */
527     void unzipingFile(String file);
528 
529     /** Puts the number of files inside zip file.
530      * @param size the number of files
531      */
532     void setZipSize(int size);
533   }
534 
535   /**
536    * Uncompresses the specified stream into the specified directory.
537    * <p>
538    * The stream contents is supposed to be a zip file.
539    * @param is  The stream for reading the zip
540    * @param dir The directory into which to unzip
541    * @throws Exception if error
542    */
543   public static void unzip(InputStream is, File dir) throws Exception {
544 
545     // Check null parameter
546     if ((dir == null) || (is == null)) {
547       throw new Exception(I18n.translate("error.null.fileparameter"));
548     }
549 
550     // Check if dir exist and is a directory */
551     if (dir.exists()) {
552       if (!dir.isDirectory()) {
553         String str = dir.getAbsolutePath();
554         throw new Exception(I18n.translate("error.notdirectory", str));
555       }
556     } else {
557       dir.mkdirs();
558     }
559 
560     BufferedInputStream bis = new BufferedInputStream(is);
561     ZipInputStream zis = new ZipInputStream(bis);
562 
563     ZipEntry ze;
564     while ((ze = zis.getNextEntry()) != null) {
565       String path = ze.getName();
566       File file = new File(dir, path);
567       if (ze.isDirectory()) {
568         file.mkdirs();
569       } else {
570         file.getParentFile().mkdirs();
571         FileOutputStream fos = null;
572         try {
573           fos = new FileOutputStream(file);
574           copyStream(fos, zis);
575         } finally {
576           close(fos);
577         }
578       }
579     }
580   }
581 
582   /**
583    * If f is a file then deletes it; if it is a directory then deletes its files
584    * and subdirectories, and after that, tries to delete it.
585    *
586    * @param f the file or directory to remove
587    * @return true if successful, false otherwise
588    */
589   public static boolean rmtree(File f) {
590     boolean result = true;
591 
592     if (f != null) {
593       if (f.isDirectory()) {
594         // Process files and subdirectories of this directory
595         File[] archivos = f.listFiles();
596         for (int x = 0; x < archivos.length; x++) {
597           if (!rmtree(archivos[x])) {
598             result = false;
599           }
600         }
601       }
602       result = result && f.delete();
603     }
604     return result;
605   }
606 
607   /**
608    * If parameter file is a file then delete this file. If file is a directory
609    * then delete yours subdirectories and files.
610    * @param dir the directory to remove
611    * @return true if successful, false otherwise
612    */
613   public static boolean rmtreeNative(File dir) {
614     if (dir == null || !dir.exists()) {
615       return true;
616     } else if (dir.isFile()) {
617       return rmtree(dir);
618     }
619 
620     String src = dir.getAbsolutePath();
621 
622     String[] cmd;
623     if (SystemUtils.isWindows()) {
624       src = src.replace('/', '\\');
625       cmd = new String[] { "cmd", "/C", "rmdir", "/S", "/Q", src };
626     } else {
627       cmd = new String[] { "sh", "-c", "rm -r '" + src + "'" };
628     }
629 
630     Integer ec = SystemUtils.execPiped(cmd, null, null);
631     if (ec == null) {
632       log.error("Unexpected error during native rmTree");
633       return false;
634     }
635     return ec.intValue() == 0;
636   }
637 
638   /**
639    * Copies a stream into another.
640    * @param dst the destination stream
641    * @param src the source stream
642    * @throws IOException if error
643    */
644   public static void copyStream(OutputStream dst, InputStream src)
645     throws IOException {
646     int retval;
647     byte[] buf = new byte[FOUR_KB_IN_BYTES];
648 
649     while ((retval = src.read(buf, 0, FOUR_KB_IN_BYTES)) != -1) {
650       dst.write(buf, 0, retval);
651     }
652     dst.flush();
653   }
654 
655   /**
656    * Copies the content of the input stream srcIn into the file dstFile.
657    * @param srcIn source input stream for the copy operation
658    * @param dstFile destination file for the copy operation
659    * @throws IOException if error
660    */
661   public static void createFile(InputStream srcIn, File dstFile) throws IOException {
662     BufferedInputStream in = null;
663     BufferedOutputStream out = null;
664     try {
665       FileOutputStream fos = new FileOutputStream(dstFile);
666       in = new BufferedInputStream(srcIn);
667       out = new BufferedOutputStream(fos);
668       byte[] buffer = new byte[THIRTY_TWO_KB_IN_BYTES];
669       int count;
670 
671       while ((count = in.read(buffer)) != -1) {
672         out.write(buffer, 0, count);
673       }
674       out.flush();
675     } finally {
676       close(in);
677       close(out);
678     }
679   }
680 
681   /**
682    * <p>Recursively copies the contents of <tt>srcDir</tt> into <tt>dstDir</tt>
683    * using external native commands.</p>
684    *
685    * <p>If <tt>dstDir</tt> does not exists, it is created.</p>
686    *
687    * @param srcDir Source directory
688    * @param dstDir Destination directory
689    * @return <tt>true</tt> if the operation is successful;
690    *         <tt>false</tt> otherwise
691    * @throws Exception If the native command could not be executed
692    */
693   public static boolean copyDirNative(File srcDir, File dstDir) throws Exception {
694 
695     // Check null parameter
696     if ((srcDir == null) || (dstDir == null)) {
697       throw new Exception(I18n.translate("error.null.fileparameter"));
698     }
699     // Creamos el directorio destino si no existe
700     if (!dstDir.exists()) {
701       dstDir.mkdirs();
702     }
703     // Start copy
704     String[] cmd;
705     if (SystemUtils.isWindows()) {
706       // Copia mediante xcopy en windows
707       String overwrite =
708         System.getProperty("os.version").equals("4.0") ? "" : "/Y";
709       cmd = new String[]{"cmd", "/c", "xcopy", overwrite, "/Q", "/E",
710         "\"" + srcDir.getAbsolutePath() + "\"",
711         "\"" + dstDir.getAbsolutePath() + "\""};
712     } else {
713       // Copia mediante cp en UNIXes
714       cmd = new String []{"sh", "-c", "cp -R '" + srcDir.getAbsolutePath()
715           + "'/* '" + dstDir.getAbsolutePath() + "'"};
716     }
717 
718     // Ejecutamos la copia
719     Integer ec = SystemUtils.execPiped(cmd, null, null);
720     if (ec == null) {
721       log.error("Unexpected error during native dir copy");
722       return false;
723     }
724 
725     int exitCode = ec.intValue();
726 
727     if ((SystemUtils.isUnix()) && (exitCode != 0)) {
728       // SJM: If the directory was empty, 'cp' will return an exit code other
729       // than one but that is not strictly an error.
730       String[] list = srcDir.list();
731       if ((list == null) || (list.length == 0)) {
732         return true;
733       }
734     }
735     return (exitCode == 0);
736   }
737 
738   /**
739    * Copies the file src_file into dst_file using native (O.S.) calls.
740    * Creates dst_dir if it does not exist.
741    * @param srcFile source file for the copy operation
742    * @param dstFile destination file for the copy operation
743    * @return true if successful, false otherwise
744    * @throws IOException If an error occurs
745    */
746   public static boolean copyNative(File srcFile, File dstFile) throws IOException {
747     // Check null parameter
748     if (srcFile == null || dstFile == null) {
749       throw new IOException(I18n.translate("error.null.fileparameter"));
750     }
751     String[] cmd;
752     boolean isWin = SystemUtils.isWindows();
753     if (isWin) {
754       cmd = new String[] { "cmd", "/C", "xcopy",
755         "\"" + srcFile.getCanonicalPath().replace('/', '\\') + "\"",
756         "\"" + dstFile.getCanonicalPath().replace('/', '\\') + "\"",
757         (System.getProperty("os.version") == "4.0") ? "" : "/y", "/i" };
758       if (!dstFile.exists()) {
759         dstFile.createNewFile();
760       }
761     } else {
762       cmd = new String[] { "sh", "-c", "cp '" + srcFile.getAbsolutePath()
763                            + "' '" + dstFile.getAbsolutePath() + "'"};
764     }
765 
766     Integer exitCode = SystemUtils.execPiped(cmd, null, null);
767     boolean ok = true;
768     if (exitCode == null) {
769       log.error("Unexpected error during native copy");
770       ok = false;
771     } else {
772       int ec = exitCode.intValue();
773       if (ec != 0) {
774         log.error("SrcFile Exist? " + srcFile.exists());
775         log.error("DstFile Exist? " + dstFile.exists());
776         log.error("Unknown error copying files(Exit Code "
777           + ec + "): " + Arrays.toString(cmd));
778         ok = false;
779       }
780     }
781     if (!ok && isWin) {
782       dstFile.delete();
783     }
784     return ok;
785   }
786 
787   /**
788    * Copia el contenido del directorio src_dir dentro del directorio dst_dir
789    * Si dst_dir no existe entonces lo crea.
790    * @param srcDir fuente
791    * @param dstDir destino
792    * @return true if successful, false otherwise
793    * @throws IOException if error
794    */
795   public static boolean copyDir(File srcDir, File dstDir)
796     throws IOException {
797     // Check null parameter
798     if ((srcDir == null) || (dstDir == null)) {
799       throw new IOException(I18n.translate("error.null.fileparameter"));
800     }
801     if (!dstDir.exists()) {
802       dstDir.mkdirs();
803     }
804     // Start copy
805     File[] archivos = srcDir.listFiles();
806     File newfile;
807     for (int x = 0; x < archivos.length; x++) {
808       newfile = new File(dstDir, archivos[x].getName());
809       if (archivos[x].isFile()) {
810         // Copy a simple file
811         try {
812           copy(archivos[x], newfile);
813         } catch (IOException ioe) {
814           return false;
815         }
816       } else {
817         // Copy a subdirectory
818         if (!copyDir(archivos[x], newfile)) {
819           return false;
820         }
821       }
822     }
823     return true;
824   }
825 
826   /**
827    * Copy a file from ffrom to fto.
828    * @param ffrom the source file
829    * @param fto the dest file
830    * @throws IOException if error
831    */
832   public static void copy(File ffrom, File fto) throws IOException {
833     // Check null parameter
834     if ((ffrom == null) || (fto == null)) {
835       throw new IOException(I18n.translate("error.null.fileparameter"));
836     }
837     // Check if f exist and is a file
838     if (!ffrom.exists()) {
839       throw new IOException(I18n.translate(
840         "error.filenotfound", new String[] {ffrom.getAbsolutePath()}));
841     }
842     // Starts copy
843     FileInputStream in = null;
844     FileOutputStream out = null;
845     try {
846       in = new FileInputStream(ffrom);
847       out = new FileOutputStream(fto);
848       byte[] buffer = new byte[THIRTY_TWO_KB_IN_BYTES];
849       int count;
850       while ((count = in.read(buffer)) != -1) {
851         out.write(buffer, 0, count);
852       }
853       out.flush();
854     } catch (Exception ex) {
855       log.error("Error copying file:" + ffrom.getAbsolutePath() + " to: "
856           + fto.getAbsolutePath(), ex);
857       throw new IOException(I18n.translate("error.write.copy",
858                                            new String[]{ffrom.getAbsolutePath(),
859                                                         fto.getAbsolutePath()}));
860     } finally {
861       close(in);
862       close(out);
863     }
864   }
865 
866   /**
867    * Moves the contents of srcFile into dstFile.
868    * @param src source file for the move operation
869    * @param dst destination file for the move operation
870    * @throws IOException if error
871    */
872   public static void move(File src, File dst) throws IOException {
873     if (!src.exists()) {
874       throw new IOException(I18n.translate("error.filenotfound", src.getAbsolutePath()));
875     }
876     copy(src, dst);
877     if (!src.delete()) {
878       throw new IOException(I18n.translate("error.write.delete", src.getAbsolutePath()));
879     }
880   }
881 
882   /**
883    * Moves a source file to a destination by calling a native command.
884    * Features of this move function:
885    *   - If the destination directory exists then moves the file within
886    *     the specified directory.
887    *   - If the destination directory does not exist then rename the original
888    *      directory/file to the one of destiny.
889    * @param fsrc The source path
890    * @param fdst The destination path
891    * @return true if successful, false otherwise
892    */
893   public static boolean moveNative(File fsrc, File fdst) {
894     String src = fsrc.getAbsolutePath();
895     String dst = fdst.getAbsolutePath();
896     String[] cmd;
897     if (SystemUtils.isWindows()) {
898       src = src.replace('/', '\\');
899       dst = dst.replace('/', '\\');
900       cmd = new String[] { "cmd", "/C", "move",  src, dst};
901     } else {
902       cmd = new String[] { "sh", "-c", "mv '" + src + "' '" + dst + "'" };
903     }
904 //  Executes the command
905     Integer exitcode = SystemUtils.execPiped(cmd, null, null);
906     if (exitcode == null) {
907       log.error("Unexpected error during native move");
908       return false;
909     }
910     return exitcode.intValue() == 0;
911   }
912 
913   /**
914    * Finds out the size of all files within the specified directory, including
915    * subdirectories. Only files accepted by the filter are included in the
916    * result.
917    *
918    * @param f the file or directory
919    * @param filter the filter for the contents of the directory
920    * @return the number of files in the directory
921    */
922   public static long getDirectorySize(File f, FileFilter filter) {
923     long size = 0;
924 
925     if (f == null) {
926       size = 0;
927     } else if (f.isFile()) {
928       if (filter == null || filter.accept(f)) {
929         size = f.length();
930       }
931     } else {
932       File[] files = (filter == null) ? f.listFiles() : f.listFiles(filter);
933       int count = (files == null) ? 0 : files.length;
934       for (int i = 0; i < count; i++) {
935         if (files[i].isFile()) {
936           size += files[i].length();
937         } else {
938           size += getDirectorySize(files[i], filter);
939         }
940       }
941     }
942     return size;
943   }
944 
945   /**
946    * Equivalent to Unix chmod command.
947    * @param perms The permissions to set. It can be in the form "u+x" or in octal
948    * @param f     The file to set permissions on
949    * @param recursive recursive behaviour
950    * @return      The exit code of the chmod execution
951    */
952   public static Integer chmod(String perms, File f, boolean recursive) {
953     if (SystemUtils.isWindows() || !f.exists()) {
954       return 0;
955     }
956     Integer ec = null;
957     try {
958       // Constructs the command line
959       String[] cmdline = { "chmod", "-f", perms, f.getAbsolutePath() };
960       if (recursive) {
961         cmdline[1] = "-fR";
962       }
963       // Executes the command
964       ec = SystemUtils.execPiped(cmdline, null, null);
965       if (ec == null) {
966         String msg = "Unknown error executing command: " + cmdline;
967         throw new Exception(msg);
968       }
969       return ec.intValue();
970 
971     } catch (Exception e) {
972       log.error("Unexpected error during native chmod", e);
973       return null;
974     }
975   }
976 
977   /**
978    * Writes the contents of a file to an stream.
979    * @param file the File that is read.
980    * @param dest the OutputStream where the contents of <code>file</code> are
981    * written.
982    * @throws IOException if error
983    */
984   public static void fileToStream(File file, OutputStream dest) throws IOException {
985     byte[] buffer = new byte[THIRTY_TWO_KB_IN_BYTES];
986     FileInputStream fis = null;
987     try {
988       fis = new FileInputStream(file);
989       int count = fis.read(buffer);
990       while (count != -1) {
991         dest.write(buffer, 0, count);
992         count = fis.read(buffer);
993       }
994       dest.flush();
995     } finally {
996       close(fis);
997     }
998   }
999 }