package com.perforce.hws.util; import java.io.IOException; import java.nio.file.FileVisitResult; import java.nio.file.FileVisitor; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.PosixFilePermission; import java.util.HashSet; import java.util.Set; import java.util.function.Predicate; /** * Extensions to the JDK 6+ NIO Files commands to help do common operations. */ public interface FilesUtils { /** * Creates a temporary directory, and if we can, makes this directory * writable only to the owner. * * @return The new directory path. * @throws IOException Signals that an I/O exception has occurred. */ default Path createTempDirectory() throws IOException { Path dir = Files.createTempDirectory("FilesUtils"); if (!System.getProperty("os.name").contains("Windows")) { setOwnerOnly(dir); } return dir; } /** * Sets the owner only. * * @param dir the new owner only * @throws IOException Signals that an I/O exception has occurred. */ default void setOwnerOnly(Path dir) throws IOException { Set<PosixFilePermission> permissions = new HashSet<>(); permissions.add(PosixFilePermission.OWNER_EXECUTE); permissions.add(PosixFilePermission.OWNER_READ); permissions.add(PosixFilePermission.OWNER_WRITE); Files.setPosixFilePermissions(dir, permissions); } /** * Used to describe your code to the withTempDir method. * <p> * Your code can throw any ol' exception, and withTempDir will probably * wrap it in a RuntimeException. */ @FunctionalInterface interface UseDirectory { /** * Use. * * @param dir the dir * @throws Exception the exception */ void use(Path dir) throws Exception; } /** * Use this to handle creating and deleting a temporary working directory * outside of the closure. * * @param useDirectory Client logic * @throws IOException Signals that an I/O exception has occurred. */ default void withTempDir(UseDirectory useDirectory) throws IOException { Path dir = createTempDirectory(); Exception captured = null; try { useDirectory.use(dir); } catch (Exception e) { captured = e; } finally { rmtree(dir.toString()); } if (captured != null) { throw new RuntimeException(captured); } } /** * Basically the equivalent of "rm -rf [path]". * * @param path the path * @throws IOException Signals that an I/O exception has occurred. */ default void rmtree(String path) throws IOException { rmtree(path, null); } /** * Will ensure removal of a file tree at the path, i.e., just like "rm -rf [path]" * * This can accept a filter that, when it resolves a path to false, we'll not * remove. If the filter is non-null, we'll ignore any errors when attempting * to delete directories. * * @param path * @param filter * @throws IOException */ default void rmtree(String path, Predicate<Path> filter) throws IOException { // This actually does a magic "retry" of directory deletion. This is // to help automate issues related to working on Windows. int retries = rmtreeRetries(); boolean deleted = false; IOException lastException = null; while (!deleted && 0 < retries) { try { Files.walkFileTree(Paths.get(path), new FileVisitor<Path>() { @Override public FileVisitResult preVisitDirectory( final Path dir, final BasicFileAttributes attrs) throws IOException { return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFile( final Path file, final BasicFileAttributes attrs) throws IOException { boolean deleteFile = true; if (filter != null) { deleteFile = filter.test(file); } if (deleteFile) { // On Windows, Files.delete will frequently fail with // an AccessDeniedException. This older behavior is // generally what we want. if (!file.toFile().delete()) { throw new IOException( "Could not delete " + file); } } return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFileFailed( final Path file, final IOException exc) throws IOException { return FileVisitResult.CONTINUE; } @Override public FileVisitResult postVisitDirectory( final Path dir, final IOException exc) throws IOException { boolean deleteDir = true; if (filter != null) { deleteDir = filter.test(dir); } if (deleteDir) { if (filter != null) { try { Files.delete(dir); } catch (IOException ignore) { } } else { Files.delete(dir); } } return FileVisitResult.CONTINUE; } }); deleted = true; } catch (IOException e) { lastException = e; try { Thread.sleep(rmtreeRetrySleepInMs()); } catch (InterruptedException e1) { throw new RuntimeException(e1); } deleted = false; } retries = retries - 1; } if (!deleted && lastException != null) { throw lastException; } } /** * Rmtree retries. * * @return the int */ default int rmtreeRetries() { return 60; } /** * Rmtree retry sleep in ms. * * @return the int */ default int rmtreeRetrySleepInMs() { return 1000; } }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#3 | 19696 | tjuricek | Add predicate option to rmtree(), and use it to ensure we don't delete local p4d config during uninstallation. | ||
#2 | 19662 | swellard |
Refactor deployment classes to make it easier to cope with multiple OS Still needs lots of tidy up |
||
#1 | 19535 | drobins | Refactor package names to hws | ||
//guest/perforce_software/helix-web-services/main/source/jdk_extensions/src/main/java/com/perforce/helix_web_services/jdk_extensions/FilesUtils.java | |||||
#6 | 19467 | tjuricek | Add basic test to ensure .deb packages can be uninstalled and re-installed without error. | ||
#5 | 19277 | tjuricek |
Add self-signed SSL configuration variation (just with the 16.1 p4d) for testing. No bugs were actually uncovered, though it may be useful to debug environments. (If people don't have a properly patched JVM with the JCE extensions, this test will fail.) |
||
#4 | 18467 | tjuricek | Windows "binary archive" distribution testing (and automation) | ||
#3 | 17296 | tjuricek | Swallow "unsupported operation" when trying to set POSIX permissions, apparently we can't do that on Windows, and, I don't have my dev environment yet. | ||
#2 | 17240 | tjuricek | Try to respond with our typical error format as 400 errors if the serverity is 3. | ||
#1 | 17140 | tjuricek |
Integrating porting work from development branch. This work is now ready for testing/CD integration. |