package org.cloudifysource.esc.util; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.util.List; import org.apache.commons.lang.StringUtils; import org.apache.commons.vfs2.FileObject; import org.apache.commons.vfs2.FileSelectInfo; import org.apache.commons.vfs2.FileSelector; import org.apache.commons.vfs2.FileSystemManager; import org.apache.commons.vfs2.FileSystemOptions; import org.apache.commons.vfs2.VFS; import org.apache.commons.vfs2.provider.sftp.SftpFileSystemConfigBuilder; import org.cloudifysource.domain.cloud.FileTransferModes; import org.cloudifysource.dsl.internal.CloudifyMessageKeys; /** * A utility class for file system handling. * * @author noak * @since 2.3.1 */ public class FileUtils { // timeout for SFTP connections private static final Integer SFTP_DISCONNECT_DETECTION_TIMEOUT_MILLIS = Integer.valueOf(10 * 1000); /** * Checks whether the files or folders exist on a remote host. The returned value depends on the last parameter - * "allMustExist". If allMustExist is True the returned value is True only if all listed objects exist. If * allMustExist is False, the returned value is True if at least one object exists. * * @param host The host to connect to * @param username The name of the user that deletes the file/folder * @param password The password of the above user * @param keyFile The key file, if used * @param fileSystemObjects The files or folders to delete * @param fileTransferMode SCP for secure copy in Linux, or CIFS for windows file sharing * @param allMustExist If set to True the function will return True only if all listed objects exist. If set to * False, the function will return True if at least one object exists. * @return depends on allMustExist * @throws IOException Indicates the deletion failed */ public static boolean fileSystemObjectsExist(final String host, final String username, final String password, final String keyFile, final List<String> fileSystemObjects, final FileTransferModes fileTransferMode, final boolean allMustExist) throws IOException { boolean objectsExist = allMustExist; if (!(fileTransferMode == FileTransferModes.SFTP)) { // TODO Support get with CIFS as well throw new IOException("File resolving is currently not supported for this file transfer protocol (" + fileTransferMode + ")"); } final FileSystemOptions opts = new FileSystemOptions(); SftpFileSystemConfigBuilder.getInstance().setStrictHostKeyChecking(opts, "no"); SftpFileSystemConfigBuilder.getInstance().setUserDirIsRoot(opts, false); if (keyFile != null && !keyFile.isEmpty()) { final File temp = new File(keyFile); if (!temp.isFile()) { throw new FileNotFoundException("Could not find key file: " + temp); } SftpFileSystemConfigBuilder.getInstance().setIdentities(opts, new File[] { temp }); } SftpFileSystemConfigBuilder.getInstance().setTimeout(opts, SFTP_DISCONNECT_DETECTION_TIMEOUT_MILLIS); final FileSystemManager mng = VFS.getManager(); String scpTargetBase, scpTarget; if (password != null && !password.isEmpty()) { scpTargetBase = "sftp://" + username + ':' + password + '@' + host; } else { scpTargetBase = "sftp://" + username + '@' + host; } FileObject remoteDir = null; try { for (final String fileSystemObject : fileSystemObjects) { scpTarget = scpTargetBase + fileSystemObject; remoteDir = mng.resolveFile(scpTarget, opts); if (remoteDir.exists()) { if (!allMustExist) { objectsExist = true; break; } } else { if (allMustExist) { objectsExist = false; break; } } } } finally { if (remoteDir != null) { mng.closeFileSystem(remoteDir.getFileSystem()); } } return objectsExist; } /** * Deletes files or folders on a remote host. * * @param host The host to connect to * @param username The name of the user that deletes the file/folder * @param password The password of the above user * @param keyFile The key file, if used * @param fileSystemObjects The files or folders to delete * @param fileTransferMode SCP for secure copy in Linux, or CIFS for windows file sharing * @throws IOException Indicates the deletion failed */ public static void deleteFileSystemObjects(final String host, final String username, final String password, final String keyFile, final List<String> fileSystemObjects, final FileTransferModes fileTransferMode) throws IOException { if (!(fileTransferMode == FileTransferModes.SFTP)) { throw new IOException("File deletion is currently not supported for this file transfer protocol (" + fileTransferMode + ")"); } final FileSystemOptions opts = new FileSystemOptions(); SftpFileSystemConfigBuilder.getInstance().setStrictHostKeyChecking(opts, "no"); SftpFileSystemConfigBuilder.getInstance().setUserDirIsRoot(opts, false); if (keyFile != null && !keyFile.isEmpty()) { final File temp = new File(keyFile); if (!temp.isFile()) { throw new FileNotFoundException("Could not find key file: " + temp); } SftpFileSystemConfigBuilder.getInstance().setIdentities(opts, new File[] { temp }); } SftpFileSystemConfigBuilder.getInstance().setTimeout(opts, SFTP_DISCONNECT_DETECTION_TIMEOUT_MILLIS); final FileSystemManager mng = VFS.getManager(); String scpTargetBase, scpTarget; if (password != null && !password.isEmpty()) { scpTargetBase = "sftp://" + username + ':' + password + '@' + host; } else { scpTargetBase = "sftp://" + username + '@' + host; } FileObject remoteDir = null; try { for (final String fileSystemObject : fileSystemObjects) { scpTarget = scpTargetBase + fileSystemObject; remoteDir = mng.resolveFile(scpTarget, opts); remoteDir.delete(new FileSelector() { @Override public boolean includeFile(final FileSelectInfo fileInfo) throws Exception { return true; } @Override public boolean traverseDescendents(final FileSelectInfo fileInfo) throws Exception { return true; } }); } } finally { if (remoteDir != null) { mng.closeFileSystem(remoteDir.getFileSystem()); } } } /** * Creates a folder a unique name based on the given basicFolderName, inside the specified parent folder. * If the folder by that name already exists in the parent folder - a number is appended to that name, until * a unique name is found. e.g.: "myfolder1", "myfolder2" ... "myfolder99" (max index is set by maxAppender). * If all those names are already in use (meaning there are existing folders with these names) - * we create a completely new random name using "File.createTempFile". * * @param parentFolder The folder to contain the new folder created by this method. * @param basicFolderName The base name to be used for the new folder. Numbers might be appended to this name to * reach uniqueness. * @param maxAppender The maximum number appended to the basic folder name to reach uniqueness. If a unique name * is not found for the folder and the maximum is reached, a new random name using "File.createTempFile". * @return The unique name * @throws IOException Indicates a failure to generate a unique folder name */ public static String createUniqueFolderName(final File parentFolder, final String basicFolderName, final int maxAppender) throws IOException { int index = 0; boolean uniqueNameFound = false; String folderName = basicFolderName; while (!uniqueNameFound && index < maxAppender) { //create a new name (temp1, temp2... temp99) folderName = basicFolderName + (++index); File restTempFolder = new File(parentFolder, folderName); if (!restTempFolder.exists()) { uniqueNameFound = true; } } if (!uniqueNameFound) { //create folder with a new unique name File tempFile = File.createTempFile(folderName, ".tmp"); tempFile.deleteOnExit(); folderName = StringUtils.substringBeforeLast(tempFile.getName(), ".tmp"); uniqueNameFound = true; } if (uniqueNameFound) { return folderName; } else { throw new IOException(CloudifyMessageKeys.UPLOAD_DIRECTORY_CREATION_FAILED.getName()); } } }