/** * Delete the entire tree. This is an internal one with slightly different behavior: if an entry * is missing, a {@link FileNotFoundException} is raised. This lets the caller distinguish a file * not found with other reasons for failure, so handles race conditions in recursive directory * deletes better. * * <p>The problem being addressed is: caller A requests a recursive directory of directory /dir ; * caller B requests a delete of a file /dir/file, between caller A enumerating the files * contents, and requesting a delete of /dir/file. We want to recognise the special case "directed * file is no longer there" and not convert that into a failure * * @param path the path to delete. * @param recursive if path is a directory and set to true, the directory is deleted else throws * an exception if the directory is not empty case of a file the recursive can be set to * either true or false. * @return true if the object was deleted * @throws IOException IO problems * @throws FileNotFoundException if a file/dir being deleted is not there - this includes entries * below the specified path, (if the path is a dir and recursive is true) */ private boolean innerDelete(Path path, boolean recursive) throws IOException { Path target = makeAbsolute(path); final FileStatus fileStatus; fileStatus = getFileStatus(path); if (LOG.isDebugEnabled()) { LOG.debug("Deleting path '" + path + "'"); } if (!SwiftUtils.isDirectory(fileStatus)) { // simple file: delete it if (LOG.isDebugEnabled()) { LOG.debug("Deleting simple file '" + path + "'"); } store.deleteObject(target); } else { // it's a directory if (LOG.isDebugEnabled()) { LOG.debug("Deleting directory '" + path + "'"); } // get all entries List<FileStatus> children = store.listDirectory(target, true, true); // look to see if there are now any children if (!children.isEmpty() && !recursive) { // if there are children, unless this is a recursive operation, fail immediately throw new SwiftOperationFailedException("Directory " + path + " is not empty."); } // delete the children for (FileStatus child : children) { Path childPath = child.getPath(); try { store.deleteObject(childPath); } catch (FileNotFoundException e) { // the path went away -race conditions. // do not fail, as the outcome is still OK. LOG.info("Path " + childPath + " is no longer present"); } } // here any children that existed have been deleted // so rm the directory (which is a no-op for /) store.rmdir(target); } return true; }
/** * Delete the entire tree. This is an internal one with slightly different behavior: if an entry * is missing, a {@link FileNotFoundException} is raised. This lets the caller distinguish a file * not found with other reasons for failure, so handles race conditions in recursive directory * deletes better. * * <p>The problem being addressed is: caller A requests a recursive directory of directory /dir ; * caller B requests a delete of a file /dir/file, between caller A enumerating the files * contents, and requesting a delete of /dir/file. We want to recognise the special case "directed * file is no longer there" and not convert that into a failure * * @param path the path to delete. * @param recursive if path is a directory and set to true, the directory is deleted else throws * an exception if the directory is not empty case of a file the recursive can be set to * either true or false. * @return true if the object was deleted * @throws IOException IO problems * @throws FileNotFoundException if a file/dir being deleted is not there - this includes entries * below the specified path, (if the path is a dir and recursive is true) */ private boolean innerDelete(Path path, boolean recursive) throws IOException { Path absolutePath = makeAbsolute(path); final FileStatus fileStatus; fileStatus = getFileStatus(path); if (LOG.isDebugEnabled()) { LOG.debug("Deleting path '" + path + "'"); } if (!SwiftUtils.isDirectory(fileStatus)) { // simple file: delete it if (LOG.isDebugEnabled()) { LOG.debug("Deleting simple file '" + path + "'"); } store.deleteObject(absolutePath); } else { // it's a directory if (LOG.isDebugEnabled()) { LOG.debug("Deleting directory '" + path + "'"); } FileStatus[] contents = listStatus(absolutePath); if (contents == null) { // the directory went away during the non-atomic stages of the operation. // Return false as it was not this thread doing the deletion. if (LOG.isDebugEnabled()) { LOG.debug("Path '" + path + "' has no status -it has 'gone away'"); } return false; } // now build a list without ourselves in it Path dirPath = fileStatus.getPath(); if (LOG.isDebugEnabled()) { LOG.debug("Found " + contents.length + " child entries under " + dirPath); } ArrayList<FileStatus> children = new ArrayList<FileStatus>(contents.length); for (FileStatus child : contents) { if (!(child.getPath().equals(dirPath))) { if (LOG.isDebugEnabled()) { LOG.debug(child.toString()); } children.add(child); } else { if (LOG.isDebugEnabled()) { LOG.debug("skipping own entry"); } } } // look to see if there are now any children if (!children.isEmpty() && !recursive) { // if there are unless this is a recursive operation, fail immediately throw new SwiftException("Directory " + path + " is not empty."); } // delete the children for (FileStatus child : children) { Path childPath = child.getPath(); try { if (!innerDelete(childPath, true)) { if (LOG.isDebugEnabled()) { LOG.debug("Failed to recursively delete '" + childPath + "'"); } return false; } } catch (FileNotFoundException e) { // the path went away -race conditions. // do not fail, as the outcome is still OK. LOG.info("Path " + childPath + " is no longer present"); } } // here any children that existed have been deleted // so rm the directory store.rmdir(absolutePath); } return true; }