Refactor ProcessWrapper to conditionally, based on System property, to forcibly destroy a forked JVM (child) process on shutdown.

This commit is contained in:
John Blum
2022-10-17 11:56:47 -07:00
parent 8afbb0c80b
commit 033d7ae7c1
4 changed files with 37 additions and 42 deletions

View File

@@ -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<ProcessInputStreamListener> 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);
}

View File

@@ -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

View File

@@ -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");

View File

@@ -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 {