From 033d7ae7c14bc1d50bdcbc5fad14e8500ec739af Mon Sep 17 00:00:00 2001 From: John Blum Date: Mon, 17 Oct 2022 11:56:47 -0700 Subject: [PATCH] Refactor ProcessWrapper to conditionally, based on System property, to forcibly destroy a forked JVM (child) process on shutdown. --- .../gemfire/tests/process/ProcessWrapper.java | 23 +++++++++++++------ .../gemfire/tests/util/FileSystemUtils.java | 23 +++++++------------ .../data/gemfire/tests/util/FileUtils.java | 21 ++++++----------- .../data/gemfire/tests/util/IOUtils.java | 12 +++++----- 4 files changed, 37 insertions(+), 42 deletions(-) diff --git a/spring-data-geode-test/src/main/java/org/springframework/data/gemfire/tests/process/ProcessWrapper.java b/spring-data-geode-test/src/main/java/org/springframework/data/gemfire/tests/process/ProcessWrapper.java index 6efcd14..62ca405 100644 --- a/spring-data-geode-test/src/main/java/org/springframework/data/gemfire/tests/process/ProcessWrapper.java +++ b/spring-data-geode-test/src/main/java/org/springframework/data/gemfire/tests/process/ProcessWrapper.java @@ -40,6 +40,7 @@ import org.springframework.data.gemfire.tests.util.IOUtils; import org.springframework.data.gemfire.tests.util.ThreadUtils; import org.springframework.data.gemfire.tests.util.ThrowableUtils; import org.springframework.lang.NonNull; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -59,6 +60,7 @@ import org.springframework.util.StringUtils; * @see java.util.concurrent.Future * @see org.springframework.data.gemfire.tests.process.ProcessConfiguration * @see org.springframework.data.gemfire.tests.process.ProcessInputStreamListener + * @see org.springframework.data.gemfire.tests.process.ProcessUtils * @since 0.0.1 */ @SuppressWarnings("unused") @@ -72,6 +74,8 @@ public class ProcessWrapper { protected static final String DEFAULT_HOST = "localhost"; + protected static final String PROCESS_DESTROY_FORCIBLY_PROPERTY = "spring.data.gemfire.test.process.destroy-forcibly"; + private final List listeners = new CopyOnWriteArrayList<>(); protected final Logger log = Logger.getLogger(getClass().getName()); @@ -220,12 +224,12 @@ public class ProcessWrapper { } } - public ProcessWrapper listeningOn(int port) { + public @NonNull ProcessWrapper listeningOn(int port) { this.port = Math.max(port, DEFAULT_PORT); return this; } - public String readLogFile() throws IOException { + public @NonNull String readLogFile() throws IOException { File[] logFiles = FileSystemUtils.listFiles(getWorkingDirectory(), path -> (path != null && (path.isDirectory() || path.getAbsolutePath().endsWith(".log")))); @@ -239,20 +243,20 @@ public class ProcessWrapper { } } - public String readLogFile(File log) throws IOException { + public @NonNull String readLogFile(@NonNull File log) throws IOException { return FileUtils.read(log); } - public boolean register(ProcessInputStreamListener listener) { + public boolean register(@Nullable ProcessInputStreamListener listener) { return listener != null && listeners.add(listener); } - public ProcessWrapper registerShutdownHook() { + public @NonNull ProcessWrapper registerShutdownHook() { Runtime.getRuntime().addShutdownHook(new Thread(this::shutdown)); return this; } - public ProcessWrapper runningOn(String host) { + public @NonNull ProcessWrapper runningOn(@Nullable String host) { this.host = StringUtils.hasText(host) ? host : DEFAULT_HOST; return this; } @@ -331,6 +335,7 @@ public class ProcessWrapper { interrupted = true; } } + } catch (TimeoutException cause) { exitValue = -1; @@ -361,7 +366,7 @@ public class ProcessWrapper { if (isRunning()) { stop(); - if (isRunning()) { + if (isRunning() && isShutdownForciblyEnabled()) { this.process.destroyForcibly(); } } @@ -369,6 +374,10 @@ public class ProcessWrapper { return safeExitValue(); } + private boolean isShutdownForciblyEnabled() { + return Boolean.getBoolean(PROCESS_DESTROY_FORCIBLY_PROPERTY); + } + public boolean unregister(ProcessInputStreamListener listener) { return this.listeners.remove(listener); } diff --git a/spring-data-geode-test/src/main/java/org/springframework/data/gemfire/tests/util/FileSystemUtils.java b/spring-data-geode-test/src/main/java/org/springframework/data/gemfire/tests/util/FileSystemUtils.java index 03707cb..bb3c2d8 100644 --- a/spring-data-geode-test/src/main/java/org/springframework/data/gemfire/tests/util/FileSystemUtils.java +++ b/spring-data-geode-test/src/main/java/org/springframework/data/gemfire/tests/util/FileSystemUtils.java @@ -30,8 +30,7 @@ import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** - * The {@link FileSystemUtils} class is a utility class encapsulating functionality to process - * file system directories and files collectively. + * Abstract utility class encapsulating functionality to process file system directories and files collectively. * * @author John Blum * @see java.io.File @@ -51,11 +50,11 @@ public abstract class FileSystemUtils extends FileUtils { public static final File[] NO_FILES = new File[0]; - public static boolean deleteRecursive(File path) { + public static boolean deleteRecursive(@Nullable File path) { return deleteRecursive(path, AllFilesFilter.INSTANCE); } - public static boolean deleteRecursive(File path, FileFilter fileFilter) { + public static boolean deleteRecursive(@Nullable File path, @Nullable FileFilter fileFilter) { boolean success = true; @@ -72,7 +71,7 @@ public abstract class FileSystemUtils extends FileUtils { return path != null && path.exists(); } - // returns sub-directory just below working directory + // returns subdirectory just below working directory public static @Nullable File getRootRelativeToWorkingDirectoryOrPath(@Nullable File path) { File localPath = path; @@ -83,12 +82,10 @@ public abstract class FileSystemUtils extends FileUtils { } } - return localPath != null - ? localPath - : path; + return localPath != null ? localPath : path; } - public static boolean isEmpty(File path) { + public static boolean isEmpty(@Nullable File path) { return isDirectory(path) ? ArrayUtils.isEmpty(path.listFiles()) @@ -120,13 +117,9 @@ public abstract class FileSystemUtils extends FileUtils { public static @NonNull File[] safeListFiles(@Nullable File directory, @Nullable FileFilter fileFilter) { - FileFilter resolvedFileFilter = fileFilter != null - ? fileFilter - : AllFilesFilter.INSTANCE; + FileFilter resolvedFileFilter = fileFilter != null ? fileFilter : AllFilesFilter.INSTANCE; - File[] files = isDirectory(directory) - ? directory.listFiles(resolvedFileFilter) - : null; + File[] files = isDirectory(directory) ? directory.listFiles(resolvedFileFilter) : null; return files != null ? files diff --git a/spring-data-geode-test/src/main/java/org/springframework/data/gemfire/tests/util/FileUtils.java b/spring-data-geode-test/src/main/java/org/springframework/data/gemfire/tests/util/FileUtils.java index 54579d1..b151d5d 100644 --- a/spring-data-geode-test/src/main/java/org/springframework/data/gemfire/tests/util/FileUtils.java +++ b/spring-data-geode-test/src/main/java/org/springframework/data/gemfire/tests/util/FileUtils.java @@ -22,17 +22,15 @@ import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; +import org.springframework.lang.NonNull; import org.springframework.util.Assert; import org.springframework.util.StringUtils; /** - * The {@link FileUtils} class is an abstract utility class for processing file system files - * by working with {@link File} objects. + * Abstract utility class for processing file system files by using {@link File} objects. * * @author John Blum * @see java.io.File - * @see java.io.FileReader - * @see java.io.FileWriter * @see org.springframework.data.gemfire.tests.util.IOUtils * @since 0.0.1 */ @@ -50,11 +48,11 @@ public abstract class FileUtils extends IOUtils { return path != null && path.isFile(); } - public static File newFile(String pathname) { + public static @NonNull File newFile(String pathname) { return new File(pathname); } - public static File newFile(File parent, String pathname) { + public static @NonNull File newFile(File parent, String pathname) { return new File(parent, pathname); } @@ -62,13 +60,11 @@ public abstract class FileUtils extends IOUtils { return path != null ? path.length() : 0L; } - public static String read(File file) throws IOException { + public static @NonNull String read(@NonNull File file) throws IOException { Assert.isTrue(isFile(file), String.format("The File [%s] to read the contents from is not a valid file", file)); - BufferedReader fileReader = new BufferedReader(new FileReader(file)); - - try { + try (BufferedReader fileReader = new BufferedReader(new FileReader(file))) { StringBuilder buffer = new StringBuilder(); @@ -79,12 +75,9 @@ public abstract class FileUtils extends IOUtils { return buffer.toString().trim(); } - finally { - close(fileReader); - } } - public static void write(File file, String contents) throws IOException { + public static void write(@NonNull File file, @NonNull String contents) throws IOException { Assert.notNull(file, "File is required"); diff --git a/spring-data-geode-test/src/main/java/org/springframework/data/gemfire/tests/util/IOUtils.java b/spring-data-geode-test/src/main/java/org/springframework/data/gemfire/tests/util/IOUtils.java index a273726..b34226c 100644 --- a/spring-data-geode-test/src/main/java/org/springframework/data/gemfire/tests/util/IOUtils.java +++ b/spring-data-geode-test/src/main/java/org/springframework/data/gemfire/tests/util/IOUtils.java @@ -13,7 +13,6 @@ * or implied. See the License for the specific language governing * permissions and limitations under the License. */ - package org.springframework.data.gemfire.tests.util; import java.io.ByteArrayInputStream; @@ -26,8 +25,11 @@ import java.io.Serializable; import java.util.logging.Level; import java.util.logging.Logger; +import org.springframework.lang.NonNull; +import org.springframework.lang.Nullable; + /** - * The {@link IOUtils} class is an abstract utility class for working with IO operations. + * Abstract utility class used to process IO operations. * * @author John Blum * @see java.io.Closeable @@ -39,7 +41,7 @@ public abstract class IOUtils { protected static final Logger log = Logger.getLogger(IOUtils.class.getName()); - public static boolean close(Closeable closeable) { + public static boolean close(@Nullable Closeable closeable) { if (closeable != null) { try { @@ -67,7 +69,7 @@ public abstract class IOUtils { * threw an {@link IOException}. * @see IOException */ - public static boolean doSafeIo(IoExceptionThrowingOperation operation) { + public static boolean doSafeIo(@NonNull IoExceptionThrowingOperation operation) { try { operation.doIo(); @@ -87,7 +89,6 @@ public abstract class IOUtils { try { objectInputStream = new ObjectInputStream(byteArrayInputStream); - return (T) objectInputStream.readObject(); } finally { @@ -105,7 +106,6 @@ public abstract class IOUtils { objectOutputStream = new ObjectOutputStream(byteArrayOutputStream); objectOutputStream.writeObject(obj); objectOutputStream.flush(); - return byteArrayOutputStream.toByteArray(); } finally {