001: /*
002: * $Id: FileUtils.java 11343 2008-03-13 10:58:26Z tcarlson $
003: * --------------------------------------------------------------------------------------
004: * Copyright (c) MuleSource, Inc. All rights reserved. http://www.mulesource.com
005: *
006: * The software in this package is published under the terms of the CPAL v1.0
007: * license, a copy of which has been included with this distribution in the
008: * LICENSE.txt file.
009: */
010:
011: package org.mule.util;
012:
013: import org.mule.MuleServer;
014: import org.mule.api.MuleRuntimeException;
015: import org.mule.config.i18n.MessageFactory;
016:
017: import java.io.BufferedOutputStream;
018: import java.io.BufferedWriter;
019: import java.io.File;
020: import java.io.FileInputStream;
021: import java.io.FileOutputStream;
022: import java.io.FileWriter;
023: import java.io.IOException;
024: import java.io.InputStream;
025: import java.io.OutputStream;
026: import java.io.UnsupportedEncodingException;
027: import java.net.JarURLConnection;
028: import java.net.URI;
029: import java.net.URL;
030: import java.net.URLConnection;
031: import java.net.URLDecoder;
032: import java.nio.channels.FileChannel;
033: import java.util.Enumeration;
034: import java.util.jar.JarEntry;
035: import java.util.jar.JarFile;
036: import java.util.zip.ZipEntry;
037: import java.util.zip.ZipFile;
038:
039: import org.apache.commons.lang.StringUtils;
040: import org.apache.commons.logging.Log;
041: import org.apache.commons.logging.LogFactory;
042:
043: /**
044: * <code>FileUtils</code> contains useful methods for dealing with files &
045: * directories.
046: */
047: // @ThreadSafe
048: public class FileUtils extends org.apache.commons.io.FileUtils {
049: private static final Log logger = LogFactory
050: .getLog(FileUtils.class);
051: public static final String DEFAULT_ENCODING = "UTF-8";
052:
053: public static synchronized void copyStreamToFile(InputStream input,
054: File destination) throws IOException {
055: if (destination.exists() && !destination.canWrite()) {
056: throw new IOException(
057: "Destination file does not exist or is not writeable");
058: }
059:
060: try {
061: FileOutputStream output = new FileOutputStream(destination);
062: try {
063: IOUtils.copy(input, output);
064: } finally {
065: IOUtils.closeQuietly(output);
066: }
067: } finally {
068: IOUtils.closeQuietly(input);
069: }
070: }
071:
072: // TODO Document me!
073: public static File createFile(String filename) throws IOException {
074: File file = FileUtils.newFile(filename);
075: if (!file.canWrite()) {
076: String dirName = file.getPath();
077: int i = dirName.lastIndexOf(File.separator);
078: if (i > -1) {
079: dirName = dirName.substring(0, i);
080: File dir = FileUtils.newFile(dirName);
081: dir.mkdirs();
082: }
083: file.createNewFile();
084: }
085: return file;
086: }
087:
088: // TODO Document me!
089: public static String prepareWinFilename(String filename) {
090: filename = filename.replaceAll("<", "(");
091: filename = filename.replaceAll(">", ")");
092: filename = filename.replaceAll("[/\\*?|:;\\]\\[\"]", "-");
093: return filename;
094: }
095:
096: // TODO Document me!
097: public static File openDirectory(String directory)
098: throws IOException {
099: File dir = FileUtils.newFile(directory);
100: if (!dir.exists()) {
101: dir.mkdirs();
102: }
103: if (!dir.isDirectory() || !dir.canRead()) {
104: throw new IOException("Path: " + directory
105: + " exists but isn't a directory");
106: }
107: return dir;
108: }
109:
110: /**
111: * Reads the incoming String into a file at at the given destination.
112: *
113: * @param filename name and path of the file to create
114: * @param data the contents of the file
115: * @return the new file.
116: * @throws IOException If the creating or writing to the file stream fails
117: */
118: public static File stringToFile(String filename, String data)
119: throws IOException {
120: return stringToFile(filename, data, false);
121: }
122:
123: // TODO Document me!
124: public static synchronized File stringToFile(String filename,
125: String data, boolean append) throws IOException {
126: return stringToFile(filename, data, append, false);
127: }
128:
129: // TODO Document me!
130: public static synchronized File stringToFile(String filename,
131: String data, boolean append, boolean newLine)
132: throws IOException {
133: File f = createFile(filename);
134: BufferedWriter writer = null;
135: try {
136: writer = new BufferedWriter(new FileWriter(f, append));
137: writer.write(data);
138: if (newLine) {
139: writer.newLine();
140: }
141: } finally {
142: if (writer != null) {
143: writer.close();
144: }
145: }
146: return f;
147: }
148:
149: // TODO Document me!
150: public static String getResourcePath(String resourceName,
151: Class callingClass) throws IOException {
152: return getResourcePath(resourceName, callingClass,
153: DEFAULT_ENCODING);
154: }
155:
156: // TODO Document me!
157: public static String getResourcePath(String resourceName,
158: Class callingClass, String encoding) throws IOException {
159: if (resourceName == null) {
160: // no name
161: return null;
162: }
163:
164: URL url = IOUtils.getResourceAsUrl(resourceName, callingClass);
165: if (url == null) {
166: // not found
167: return null;
168: }
169: return normalizeFilePath(url, encoding);
170: }
171:
172: /**
173: * Remove from uri to file prefix file:/
174: * Add if need file separator to begin
175: *
176: * @param url file uri to resource
177: * @param encoding - Java encoding names
178: * @return normalized file path
179: * @throws UnsupportedEncodingException if encoding is unknown
180: */
181: public static String normalizeFilePath(URL url, String encoding)
182: throws UnsupportedEncodingException {
183: String resource = URLDecoder.decode(url.toExternalForm(),
184: encoding);
185: if (resource != null) {
186: if (resource.startsWith("file:/")) {
187: resource = resource.substring(6);
188: }
189: if (!resource.startsWith(File.separator)) {
190: resource = File.separator + resource;
191: }
192: }
193: return resource;
194: }
195:
196: /**
197: * Delete a file tree recursively.
198: * @param dir dir to wipe out
199: * @return false when the first unsuccessful attempt encountered
200: */
201: public static boolean deleteTree(File dir) {
202: return deleteTree(dir, null);
203: }
204:
205: /**
206: * Delete a file tree recursively. This method additionally tries to be
207: * gentle with specified top-level dirs. E.g. this is the case when a
208: * transaction manager asynchronously handles the recovery log, and the test
209: * wipes out everything, leaving the transaction manager puzzled.
210: * @param dir dir to wipe out
211: * @param topLevelDirsToIgnore which top-level directories to ignore,
212: * if null or empty then ignored
213: * @return false when the first unsuccessful attempt encountered
214: */
215: public static boolean deleteTree(File dir,
216: final String[] topLevelDirsToIgnore) {
217: if (dir == null || !dir.exists()) {
218: return true;
219: }
220: File[] files = dir.listFiles();
221: if (files != null) {
222: for (int i = 0; i < files.length; i++) {
223: OUTER: if (files[i].isDirectory()) {
224: if (topLevelDirsToIgnore != null) {
225: for (int j = 0; j < topLevelDirsToIgnore.length; j++) {
226: String ignored = topLevelDirsToIgnore[j];
227: if (ignored.equals(FilenameUtils
228: .getBaseName(files[i].getName()))) {
229: break OUTER;
230: }
231: }
232: }
233: if (!deleteTree(files[i])) {
234: return false;
235: }
236: } else {
237: if (!files[i].delete()) {
238: return false;
239: }
240: }
241: }
242: }
243: return dir.delete();
244: }
245:
246: /**
247: * Unzip the specified archive to the given directory
248: */
249: public static void unzip(File archive, File directory)
250: throws IOException {
251: ZipFile zip = null;
252:
253: if (directory.exists()) {
254: if (!directory.isDirectory()) {
255: throw new IOException("Directory is not a directory: "
256: + directory);
257: }
258: } else {
259: if (!directory.mkdirs()) {
260: throw new IOException("Could not create directory: "
261: + directory);
262: }
263: }
264: try {
265: zip = new ZipFile(archive);
266: for (Enumeration entries = zip.entries(); entries
267: .hasMoreElements();) {
268: ZipEntry entry = (ZipEntry) entries.nextElement();
269: File f = FileUtils.newFile(directory, entry.getName());
270: if (entry.isDirectory()) {
271: if (!f.mkdirs()) {
272: throw new IOException(
273: "Could not create directory: " + f);
274: }
275: } else {
276: InputStream is = zip.getInputStream(entry);
277: OutputStream os = new BufferedOutputStream(
278: new FileOutputStream(f));
279: IOUtils.copy(is, os);
280: IOUtils.closeQuietly(is);
281: IOUtils.closeQuietly(os);
282: }
283: }
284: } finally {
285: if (zip != null) {
286: zip.close();
287: }
288: }
289: }
290:
291: /**
292: * Workaround for JDK bug <a href="http://bugs.sun.com/bugdatabase/view_bug.do;:YfiG?bug_id=4117557">
293: * 4117557</a>. More in-context information at
294: * <a href="http://mule.mulesource.org/jira/browse/MULE-1112">MULE-1112</a>
295: * <p/>
296: * Factory methods correspond to constructors of the <code>java.io.File class</code>.
297: * No physical file created in this method.
298: *
299: * @see File
300: */
301: public static File newFile(String pathName) {
302: try {
303: return new File(pathName).getCanonicalFile();
304: } catch (IOException e) {
305: throw new MuleRuntimeException(
306: MessageFactory
307: .createStaticMessage("Unable to create a canonical file for "
308: + pathName), e);
309: }
310: }
311:
312: /**
313: * Workaround for JDK bug <a href="http://bugs.sun.com/bugdatabase/view_bug.do;:YfiG?bug_id=4117557">
314: * 4117557</a>. More in-context information at
315: * <a href="http://mule.mulesource.org/jira/browse/MULE-1112">MULE-1112</a>
316: * <p/>
317: * Factory methods correspond to constructors of the <code>java.io.File class</code>.
318: * No physical file created in this method.
319: *
320: * @see File
321: */
322: public static File newFile(URI uri) {
323: try {
324: return new File(uri).getCanonicalFile();
325: } catch (IOException e) {
326: throw new MuleRuntimeException(
327: MessageFactory
328: .createStaticMessage("Unable to create a canonical file for "
329: + uri), e);
330: }
331: }
332:
333: /**
334: * Workaround for JDK bug <a href="http://bugs.sun.com/bugdatabase/view_bug.do;:YfiG?bug_id=4117557">
335: * 4117557</a>. More in-context information at
336: * <a href="http://mule.mulesource.org/jira/browse/MULE-1112">MULE-1112</a>
337: * <p/>
338: * Factory methods correspond to constructors of the <code>java.io.File class</code>.
339: * No physical file created in this method.
340: *
341: * @see File
342: */
343: public static File newFile(File parent, String child) {
344: try {
345: return new File(parent, child).getCanonicalFile();
346: } catch (IOException e) {
347: throw new MuleRuntimeException(
348: MessageFactory
349: .createStaticMessage("Unable to create a canonical file for parent: "
350: + parent + " and child: " + child),
351: e);
352: }
353: }
354:
355: /**
356: * Workaround for JDK bug <a href="http://bugs.sun.com/bugdatabase/view_bug.do;:YfiG?bug_id=4117557">
357: * 4117557</a>. More in-context information at
358: * <a href="http://mule.mulesource.org/jira/browse/MULE-1112">MULE-1112</a>
359: * <p/>
360: * Factory methods correspond to constructors of the <code>java.io.File class</code>.
361: * No physical file created in this method.
362: *
363: * @see File
364: */
365: public static File newFile(String parent, String child) {
366: try {
367: return new File(parent, child).getCanonicalFile();
368: } catch (IOException e) {
369: throw new MuleRuntimeException(
370: MessageFactory
371: .createStaticMessage("Unable to create a canonical file for parent: "
372: + parent + " and child: " + child),
373: e);
374: }
375: }
376:
377: /**
378: * Extract the specified resource to the given directory for
379: * remain all directory struct
380: *
381: * @param resourceName - full resource name
382: * @param callingClass - classloader for this class is used
383: * @param outputDir - extract to this directory
384: * @param keepParentDirectory true - full structure of directories is kept; false - file - removed all directories, directory - started from resource point
385: * @throws IOException if any errors
386: */
387: public static void extractResources(String resourceName,
388: Class callingClass, File outputDir,
389: boolean keepParentDirectory) throws IOException {
390: URL url = callingClass.getClassLoader().getResource(
391: resourceName);
392: URLConnection connection = url.openConnection();
393: if (connection instanceof JarURLConnection) {
394: extractJarResources((JarURLConnection) connection,
395: outputDir, keepParentDirectory);
396: } else {
397: extractFileResources(normalizeFilePath(url, MuleServer
398: .getMuleContext().getConfiguration()
399: .getDefaultEncoding()), outputDir, resourceName,
400: keepParentDirectory);
401: }
402: }
403:
404: /**
405: * Extract resources contain in file
406: *
407: * @param path - path to file
408: * @param outputDir Directory for unpack recources
409: * @param resourceName
410: * @param keepParentDirectory true - full structure of directories is kept; false - file - removed all directories, directory - started from resource point
411: * @throws IOException if any error
412: */
413: private static void extractFileResources(String path,
414: File outputDir, String resourceName,
415: boolean keepParentDirectory) throws IOException {
416: File file = FileUtils.newFile(path);
417: if (!file.exists()) {
418: throw new IOException("The resource by path " + path + " ");
419: }
420: if (file.isDirectory()) {
421: if (keepParentDirectory) {
422: outputDir = FileUtils.newFile(outputDir.getPath()
423: + File.separator + resourceName);
424: if (!outputDir.exists()) {
425: outputDir.mkdirs();
426: }
427: } else {
428: outputDir = FileUtils.newFile(outputDir.getPath());
429: }
430: copyDirectory(file, outputDir);
431: } else {
432:
433: if (keepParentDirectory) {
434: outputDir = FileUtils.newFile(outputDir.getPath()
435: + File.separator + resourceName);
436: } else {
437: outputDir = FileUtils.newFile(outputDir.getPath()
438: + File.separator + file.getName());
439: }
440: copyFile(file, outputDir);
441: }
442: }
443:
444: /**
445: * Extract recources contain if jar (have to in classpath)
446: *
447: * @param connection JarURLConnection to jar library
448: * @param outputDir Directory for unpack recources
449: * @param keepParentDirectory true - full structure of directories is kept; false - file - removed all directories, directory - started from resource point
450: * @throws IOException if any error
451: */
452: private static void extractJarResources(
453: JarURLConnection connection, File outputDir,
454: boolean keepParentDirectory) throws IOException {
455: JarFile jarFile = connection.getJarFile();
456: JarEntry jarResource = connection.getJarEntry();
457: Enumeration entries = jarFile.entries();
458: InputStream inputStream = null;
459: OutputStream outputStream = null;
460: int jarResourceNameLenght = jarResource.getName().length();
461: for (; entries.hasMoreElements();) {
462: JarEntry entry = (JarEntry) entries.nextElement();
463: if (entry.getName().startsWith(jarResource.getName())) {
464:
465: String path = outputDir.getPath() + File.separator
466: + entry.getName();
467:
468: //remove directory struct for file and first dir for directory
469: if (!keepParentDirectory) {
470: if (entry.isDirectory()) {
471: if (entry.getName().equals(
472: jarResource.getName())) {
473: continue;
474: }
475: path = outputDir.getPath()
476: + File.separator
477: + entry.getName().substring(
478: jarResourceNameLenght,
479: entry.getName().length());
480: } else {
481: if (entry.getName().length() > jarResourceNameLenght) {
482: path = outputDir.getPath()
483: + File.separator
484: + entry.getName().substring(
485: jarResourceNameLenght,
486: entry.getName().length());
487: } else {
488: path = outputDir.getPath()
489: + File.separator
490: + entry.getName().substring(
491: entry.getName()
492: .lastIndexOf("/"),
493: entry.getName().length());
494: }
495: }
496: }
497:
498: File file = FileUtils.newFile(path);
499: if (!file.getParentFile().exists()) {
500: if (!file.getParentFile().mkdirs()) {
501: throw new IOException(
502: "Could not create directory: "
503: + file.getParentFile());
504: }
505: }
506: if (entry.isDirectory()) {
507: if (!file.exists() && !file.mkdirs()) {
508: throw new IOException(
509: "Could not create directory: " + file);
510: }
511:
512: } else {
513: try {
514: inputStream = jarFile.getInputStream(entry);
515: outputStream = new BufferedOutputStream(
516: new FileOutputStream(file));
517: IOUtils.copy(inputStream, outputStream);
518: } finally {
519: IOUtils.closeQuietly(inputStream);
520: IOUtils.closeQuietly(outputStream);
521: }
522: }
523:
524: }
525: }
526: }
527:
528: public static boolean renameFileHard(String srcFilePath,
529: String destFilePath) {
530: if (StringUtils.isNotBlank(srcFilePath)
531: && StringUtils.isNotBlank(destFilePath)) {
532: return renameFileHard(new File(srcFilePath), new File(
533: destFilePath));
534: } else {
535: return false;
536: }
537: }
538:
539: public static boolean renameFileHard(File srcFile, File destFile) {
540: boolean isRenamed = false;
541: if (srcFile != null && destFile != null) {
542: logger.debug("Moving file " + srcFile.getAbsolutePath()
543: + " to " + destFile.getAbsolutePath());
544: if (!destFile.exists()) {
545: try {
546: if (srcFile.isFile()) {
547: logger.debug("Trying to rename file");
548: FileInputStream in = null;
549: FileOutputStream out = null;
550: try {
551: in = new FileInputStream(srcFile);
552: out = new FileOutputStream(destFile);
553: out.getChannel().transferFrom(
554: in.getChannel(), 0,
555: srcFile.length());
556: isRenamed = true;
557: } catch (Exception e) {
558: logger.debug(e);
559: } finally {
560: if (in != null) {
561: try {
562: in.close();
563: } catch (Exception inNotClosed) {
564: logger.debug(inNotClosed);
565: }
566: }
567: if (out != null) {
568: try {
569: out.close();
570: } catch (Exception outNotClosed) {
571: logger.debug(outNotClosed);
572: }
573: }
574: }
575: logger.debug("File renamed: " + isRenamed);
576: if (isRenamed) {
577: srcFile.delete();
578: } else {
579: destFile.delete();
580: }
581: } else {
582: logger.debug(srcFile.getAbsolutePath()
583: + " is not a valid file.");
584: }
585: } catch (Exception e) {
586: logger.debug("Error renaming file from "
587: + srcFile.getAbsolutePath() + " to "
588: + destFile.getAbsolutePath());
589: }
590: } else {
591: logger.debug("Error renaming file "
592: + srcFile.getAbsolutePath()
593: + ". Destination file "
594: + destFile.getAbsolutePath()
595: + " already exists.");
596: }
597: }
598: return isRenamed;
599: }
600:
601: public static boolean renameFile(String srcFilePath,
602: String destFilePath) {
603: if (StringUtils.isNotBlank(srcFilePath)
604: && StringUtils.isNotBlank(destFilePath)) {
605: return renameFile(new File(srcFilePath), new File(
606: destFilePath));
607: } else {
608: return false;
609: }
610: }
611:
612: public static boolean renameFile(File srcFile, File destFile) {
613: boolean isRenamed = false;
614: if (srcFile != null && destFile != null) {
615: logger.debug("Moving file " + srcFile.getAbsolutePath()
616: + " to " + destFile.getAbsolutePath());
617: if (!destFile.exists()) {
618: try {
619: if (srcFile.isFile()) {
620: logger.debug("Trying to rename file");
621: isRenamed = srcFile.renameTo(destFile);
622: if (!isRenamed && srcFile.exists()) {
623: logger
624: .debug("Trying hard copy, assuming partition crossing ...");
625: isRenamed = renameFileHard(srcFile,
626: destFile);
627: }
628: logger.debug("File renamed: " + isRenamed);
629: } else {
630: logger.debug(srcFile.getAbsolutePath()
631: + " is not a valid file");
632: }
633: } catch (Exception e) {
634: logger.debug("Error moving file from "
635: + srcFile.getAbsolutePath() + " to "
636: + destFile.getAbsolutePath(), e);
637: }
638: } else {
639: logger.debug("Error renaming file "
640: + srcFile.getAbsolutePath()
641: + ". Destination file "
642: + destFile.getAbsolutePath()
643: + " already exists.");
644: }
645: } else {
646: logger
647: .debug("Error renaming file. Source or destination file is null.");
648: }
649:
650: return isRenamed;
651: }
652:
653: /** Try to move a file by renaming with backup attempt by copying/deleting via NIO */
654: public static boolean moveFile(File sourceFile, File destinationFile) {
655: // try fast file-system-level move/rename first
656: boolean success = sourceFile.renameTo(destinationFile);
657:
658: if (!success) {
659: // try again using NIO copy
660: FileInputStream fis = null;
661: FileOutputStream fos = null;
662: try {
663: fis = new FileInputStream(sourceFile);
664: fos = new FileOutputStream(destinationFile);
665: FileChannel srcChannel = fis.getChannel();
666: FileChannel dstChannel = fos.getChannel();
667: dstChannel.transferFrom(srcChannel, 0, srcChannel
668: .size());
669: srcChannel.close();
670: dstChannel.close();
671: success = sourceFile.delete();
672: } catch (IOException ioex) {
673: // grr!
674: success = false;
675: } finally {
676: IOUtils.closeQuietly(fis);
677: IOUtils.closeQuietly(fos);
678: }
679: }
680:
681: return success;
682: }
683:
684: }
|