Review, refactor and polish all Process classes in the org.springframework.data.gemfire.tests.process package.

This commit is contained in:
John Blum
2018-03-22 10:24:54 -07:00
parent e85d10108a
commit c928f29298
6 changed files with 152 additions and 162 deletions

View File

@@ -17,29 +17,28 @@
package org.springframework.data.gemfire.tests.process;
/**
* The PidUnavailableException class is a RuntimeException indicating that the process ID (PID) is unobtainable for
* the current process.
* The {@link PidNotFoundException} class is a {@link RuntimeException} indicating that the process ID (PID)
* is unobtainable for the current {@link Process}.
*
* @author John Blum
* @see RuntimeException
* @since 1.5.0
* @see java.lang.RuntimeException
* @since 0.0.1
*/
@SuppressWarnings("unused")
public class PidUnavailableException extends RuntimeException {
public class PidNotFoundException extends RuntimeException {
public PidUnavailableException() {
public PidNotFoundException() {
}
public PidUnavailableException(final String message) {
public PidNotFoundException(final String message) {
super(message);
}
public PidUnavailableException(final Throwable cause) {
public PidNotFoundException(final Throwable cause) {
super(cause);
}
public PidUnavailableException(final String message, final Throwable cause) {
public PidNotFoundException(final String message, final Throwable cause) {
super(message, cause);
}
}

View File

@@ -32,9 +32,10 @@ import org.springframework.util.StringUtils;
* for a running process.
*
* @author John Blum
* @see ProcessBuilder
* @see org.springframework.data.gemfire.process.ProcessExecutor
* @since 1.5.0
* @see java.lang.Process
* @see java.lang.ProcessBuilder
* @see org.springframework.data.gemfire.tests.process.ProcessExecutor
* @since 0.0.1
*/
@SuppressWarnings("unused")
public class ProcessConfiguration {
@@ -48,7 +49,9 @@ public class ProcessConfiguration {
private final Map<String, String> environment;
public static ProcessConfiguration create(ProcessBuilder processBuilder) {
Assert.notNull(processBuilder, "The ProcessBuilder used to configure and start the Process must not be null");
Assert.notNull(processBuilder,
"The ProcessBuilder used to configure and start the Process must not be null");
return new ProcessConfiguration(processBuilder.command(), processBuilder.directory(),
processBuilder.environment(), processBuilder.redirectErrorStream());
@@ -57,22 +60,22 @@ public class ProcessConfiguration {
public ProcessConfiguration(List<String> command, File workingDirectory, Map<String, String> environment,
boolean redirectErrorStream) {
Assert.notEmpty(command, "Process command must be specified");
Assert.notEmpty(command, "Process command is required");
Assert.isTrue(FileSystemUtils.isDirectory(workingDirectory), String.format(
"Process working directory [%s] is not valid", workingDirectory));
Assert.isTrue(FileSystemUtils.isDirectory(workingDirectory),
String.format("Process working directory [%s] is not valid", workingDirectory));
this.command = new ArrayList<String>(command);
this.command = Collections.unmodifiableList(new ArrayList<>(command));
this.workingDirectory = workingDirectory;
this.redirectingErrorStream = redirectErrorStream;
this.environment = (environment != null
? Collections.unmodifiableMap(new HashMap<String, String>(environment))
: Collections.<String, String>emptyMap());
this.environment = environment != null
? Collections.unmodifiableMap(new HashMap<>(environment))
: Collections.emptyMap();
}
public List<String> getCommand() {
return Collections.unmodifiableList(command);
return this.command;
}
public String getCommandString() {
@@ -80,19 +83,20 @@ public class ProcessConfiguration {
}
public Map<String, String> getEnvironment() {
return environment;
return this.environment;
}
public boolean isRedirectingErrorStream() {
return redirectingErrorStream;
return this.redirectingErrorStream;
}
public File getWorkingDirectory() {
return workingDirectory;
return this.workingDirectory;
}
@Override
public String toString() {
return "{ command = ".concat(getCommandString())
.concat(", workingDirectory = ".concat(getWorkingDirectory().getAbsolutePath()))
.concat(", environment = ".concat(String.valueOf(getEnvironment())))

View File

@@ -39,7 +39,7 @@ import org.springframework.util.StringUtils;
* @see java.lang.System
* @see org.springframework.data.gemfire.tests.process.ProcessConfiguration
* @see org.springframework.data.gemfire.tests.process.ProcessWrapper
* @since 1.5.0
* @since 0.0.1
*/
@SuppressWarnings("unused")
public abstract class ProcessExecutor {
@@ -48,8 +48,8 @@ public abstract class ProcessExecutor {
public static final String JAVA_CLASSPATH = System.getProperty("java.class.path");
protected static final String SPRING_GEMFIRE_SYSTEM_PROPERTY_PREFIX = "spring.gemfire.";
protected static final String SPRING_DATA_GEMFIRE_SYSTEM_PROPERTY_PREFIX = "spring.data.gemfire.";
protected static final String SPRING_GEMFIRE_SYSTEM_PROPERTY_PREFIX = "spring.gemfire.";
public static ProcessWrapper launch(Class<?> type, String... args) throws IOException {
return launch(FileSystemUtils.WORKING_DIRECTORY, type, args);
@@ -77,6 +77,7 @@ public abstract class ProcessExecutor {
}
protected static String[] buildCommand(String classpath, Class<?> type, String... args) {
Assert.notNull(type, "The main Java class to launch must not be null");
List<String> command = new ArrayList<>();
@@ -93,7 +94,7 @@ public abstract class ProcessExecutor {
if (isJvmOption(arg)) {
command.add(arg);
}
else if (!StringUtils.isEmpty(arg)) {
else if (isValidArgument(arg)) {
programArguments.add(arg);
}
}
@@ -105,6 +106,7 @@ public abstract class ProcessExecutor {
}
protected static Collection<? extends String> getSpringGemFireSystemProperties() {
return System.getProperties().stringPropertyNames().stream()
.filter(property -> property.startsWith(SPRING_DATA_GEMFIRE_SYSTEM_PROPERTY_PREFIX)
|| property.startsWith(SPRING_GEMFIRE_SYSTEM_PROPERTY_PREFIX))
@@ -116,7 +118,12 @@ public abstract class ProcessExecutor {
return (StringUtils.hasText(option) && (option.startsWith("-D") || option.startsWith("-X")));
}
protected static boolean isValidArgument(String argument) {
return StringUtils.hasText(argument);
}
protected static File validateDirectory(File workingDirectory) {
Assert.isTrue(workingDirectory != null && (workingDirectory.isDirectory() || workingDirectory.mkdirs()),
String.format("Failed to create working directory [%s]", workingDirectory));

View File

@@ -23,8 +23,8 @@ import java.util.EventListener;
* {@link Process process's} standard output steam or standard error stream.
*
* @author John Blum
* @see EventListener
* @since 1.5.0
* @see java.util.EventListener
* @since 0.0.1
*/
public interface ProcessInputStreamListener extends EventListener {
@@ -33,8 +33,8 @@ public interface ProcessInputStreamListener extends EventListener {
* or standard error streams.
*
* @param input {@link String} containing output from the {@link Process} that this listener is listening to.
* @see Process#getErrorStream()
* @see Process#getInputStream()
* @see java.lang.Process#getErrorStream()
* @see java.lang.Process#getInputStream()
*/
void onInput(String input);

View File

@@ -42,7 +42,7 @@ import org.springframework.util.StringUtils;
* @author John Blum
* @see File
* @see Process
* @since 1.5.0
* @since 0.0.1
*/
@SuppressWarnings("unused")
public abstract class ProcessUtils {
@@ -53,12 +53,15 @@ public abstract class ProcessUtils {
/* (non-Javadoc) */
public static int currentPid() {
RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
String runtimeMXBeanName = runtimeMXBean.getName();
Exception cause = null;
if (StringUtils.hasText(runtimeMXBeanName)) {
int atSignIndex = runtimeMXBeanName.indexOf('@');
if (atSignIndex > 0) {
@@ -71,32 +74,20 @@ public abstract class ProcessUtils {
}
}
throw new PidUnavailableException(String.format("Process ID (PID) not available [%s]", runtimeMXBeanName),
cause);
throw new PidNotFoundException(String.format("Process ID (PID) not available [%s]",
runtimeMXBeanName), cause);
}
/* (non-Javadoc) */
public static boolean isAlive(Process process) {
return (process != null && process.isAlive());
return process != null && process.isAlive();
}
/* (non-Javadoc) */
public static boolean isRunning(int processId) {
/*
for (VirtualMachineDescriptor vmDescriptor : VirtualMachine.list()) {
if (String.valueOf(processId).equals(vmDescriptor.id())) {
return true;
}
}
return false;
*/
throw new UnsupportedOperationException("operation not supported");
throw new UnsupportedOperationException("Operation not supported");
}
/* (non-Javadoc) */
public static boolean isRunning(Process process) {
try {
process.exitValue();
return false;
@@ -106,40 +97,24 @@ public abstract class ProcessUtils {
}
}
/* (non-Javadoc) */
public static void signalStop(Process process) throws IOException {
if (isRunning(process)) {
OutputStream processOutputStream = process.getOutputStream();
processOutputStream.write(TERM_TOKEN.concat("\n").getBytes());
processOutputStream.flush();
}
}
/* (non-Javadoc) */
@SuppressWarnings("all")
public static void waitForStopSignal() {
Scanner in = new Scanner(System.in);
while (!TERM_TOKEN.equals(in.next()));
}
/* (non-Javadoc) */
public static int findAndReadPid(File workingDirectory) {
File pidFile = findPidFile(workingDirectory);
if (pidFile == null) {
throw new PidUnavailableException(String.format(
"No PID file was found in working directory [%s] or any of it's sub-directories",
throw new PidNotFoundException(
String.format("No PID file was found in working directory [%s] or any of it's sub-directories",
workingDirectory));
}
return readPid(pidFile);
}
/* (non-Javadoc) */
@SuppressWarnings("all")
protected static File findPidFile(File workingDirectory) {
Assert.isTrue(FileSystemUtils.isDirectory(workingDirectory), String.format(
"File [%s] is not a valid directory", workingDirectory));
Assert.isTrue(FileSystemUtils.isDirectory(workingDirectory),
String.format("File [%s] is not a valid directory", workingDirectory));
for (File file : workingDirectory.listFiles(DirectoryPidFileFilter.INSTANCE)) {
if (file.isDirectory()) {
@@ -154,45 +129,48 @@ public abstract class ProcessUtils {
return null;
}
/* (non-Javadoc) */
@SuppressWarnings("all")
public static int readPid(File pidFile) {
Assert.isTrue(pidFile != null && pidFile.isFile(), String.format(
"File [%s] is not a valid file", pidFile));
Assert.isTrue(pidFile != null && pidFile.isFile(),
String.format("File [%s] is not a valid file", pidFile));
BufferedReader fileReader = null;
String pidValue = null;
try {
fileReader = new BufferedReader(new FileReader(pidFile));
pidValue = String.valueOf(fileReader.readLine()).trim();
return Integer.parseInt(pidValue);
}
catch (FileNotFoundException e) {
throw new PidUnavailableException(String.format("PID file [%s] not found", pidFile), e);
catch (FileNotFoundException cause) {
throw new PidNotFoundException(String.format("PID file [%s] not found", pidFile), cause);
}
catch (IOException e) {
throw new PidUnavailableException(String.format("failed to read PID from file [%s]", pidFile), e);
catch (IOException cause) {
throw new PidNotFoundException(String.format("Failed to read PID from file [%s]", pidFile), cause);
}
catch (NumberFormatException e) {
throw new PidUnavailableException(String.format(
"value [%1$s] from PID file [%2$s] was not a valid numerical PID", pidValue, pidFile), e);
catch (NumberFormatException cause) {
throw new PidNotFoundException(String.format("Value [%1$s] from PID file [%2$s] was not a valid numerical PID",
pidValue, pidFile), cause);
}
finally {
IOUtils.close(fileReader);
}
}
/* (non-Javadoc) */
@SuppressWarnings("all")
public static void writePid(File pidFile, int pid) throws IOException {
Assert.isTrue(pidFile != null && (pidFile.isFile() || pidFile.createNewFile()), String.format(
"File [%s] is not a valid file", pidFile));
Assert.isTrue(pidFile != null && (pidFile.isFile() || pidFile.createNewFile()),
String.format("File [%s] is not a valid file", pidFile));
Assert.isTrue(pid > 0, String.format("PID [%d] must greater than 0", pid));
PrintWriter fileWriter = new PrintWriter(new BufferedWriter(new FileWriter(pidFile, false), 16), true);
PrintWriter fileWriter = new PrintWriter(new BufferedWriter(
new FileWriter(pidFile, false), 16), true);
try {
fileWriter.println(pid);
@@ -203,28 +181,37 @@ public abstract class ProcessUtils {
}
}
/* (non-Javadoc) */
public static void signalStop(Process process) throws IOException {
if (isRunning(process)) {
OutputStream processOutputStream = process.getOutputStream();
processOutputStream.write(TERM_TOKEN.concat("\n").getBytes());
processOutputStream.flush();
}
}
@SuppressWarnings("all")
public static void waitForStopSignal() {
Scanner in = new Scanner(System.in);
while (!TERM_TOKEN.equals(in.next()));
}
protected static class DirectoryPidFileFilter extends PidFileFilter {
protected static final DirectoryPidFileFilter INSTANCE = new DirectoryPidFileFilter();
/**
* @inheritDoc
*/
@Override
public boolean accept(File path) {
return (path != null && (path.isDirectory() || super.accept(path)));
}
}
/* (non-Javadoc) */
protected static class PidFileFilter implements FileFilter {
protected static final PidFileFilter INSTANCE = new PidFileFilter();
/**
* @inheritDoc
*/
@Override
public boolean accept(File path) {
return (path != null && path.isFile() && path.getName().toLowerCase().endsWith(".pid"));

View File

@@ -49,7 +49,7 @@ import org.springframework.util.Assert;
* @author John Blum
* @see Process
* @see ProcessBuilder
* @since 1.5.0
* @since 0.0.1
*/
@SuppressWarnings("unused")
public class ProcessWrapper {
@@ -63,15 +63,16 @@ public class ProcessWrapper {
protected final Logger log = Logger.getLogger(getClass().getName());
private final Process process;
private final ProcessConfiguration processConfiguration;
/* (non-Javadoc) */
public ProcessWrapper(Process process, ProcessConfiguration processConfiguration) {
Assert.notNull(process, "Process must not be null");
Assert.notNull(process, "Process is required");
Assert.notNull(processConfiguration, "The context and configuration meta-data providing details"
+ " about the environment in which the process is running and how the process was configured and executed"
+ " must not be null");
+ " is required");
this.process = process;
this.processConfiguration = processConfiguration;
@@ -79,8 +80,8 @@ public class ProcessWrapper {
init();
}
/* (non-Javadoc) */
private void init() {
newThread("Process OUT Stream Reader Thread",
newProcessInputStreamReaderRunnable(process.getInputStream())).start();
@@ -90,15 +91,17 @@ public class ProcessWrapper {
}
}
/* (non-Javadoc) */
protected Runnable newProcessInputStreamReaderRunnable(InputStream in) {
private Runnable newProcessInputStreamReaderRunnable(InputStream in) {
return () -> {
if (isRunning()) {
BufferedReader inputReader = new BufferedReader(new InputStreamReader(in));
try {
for (String input = inputReader.readLine(); input != null; input = inputReader.readLine()) {
for (ProcessInputStreamListener listener : listeners) {
for (ProcessInputStreamListener listener : this.listeners) {
listener.onInput(input);
}
}
@@ -114,10 +117,10 @@ public class ProcessWrapper {
};
}
/* (non-Javadoc) */
protected Thread newThread(String name, Runnable task) {
Assert.hasText(name, "Thread name must be specified");
Assert.notNull(task, "Thread task must not be null");
private Thread newThread(String name, Runnable task) {
Assert.hasText(name, "Thread name is required");
Assert.notNull(task, "Thread task is required");
Thread thread = new Thread(task, name);
@@ -127,73 +130,62 @@ public class ProcessWrapper {
return thread;
}
/* (non-Javadoc) */
public boolean isAlive() {
return ProcessUtils.isAlive(process);
}
/* (non-Javadoc) */
public boolean isNotAlive() {
return !isAlive();
}
/* (non-Javadoc) */
public List<String> getCommand() {
return processConfiguration.getCommand();
return this.processConfiguration.getCommand();
}
/* (non-Javadoc) */
public String getCommandString() {
return processConfiguration.getCommandString();
return this.processConfiguration.getCommandString();
}
/* (non-Javadoc) */
public Map<String, String> getEnvironment() {
return processConfiguration.getEnvironment();
return this.processConfiguration.getEnvironment();
}
/* (non-Javadoc) */
public int getPid() {
return ProcessUtils.findAndReadPid(getWorkingDirectory());
}
/* (non-Javadoc) */
public int safeGetPid() {
try {
return getPid();
}
catch (PidUnavailableException ignore) {
catch (PidNotFoundException ignore) {
return -1;
}
}
/* (non-Javadoc) */
public boolean isRedirectingErrorStream() {
return processConfiguration.isRedirectingErrorStream();
return this.processConfiguration.isRedirectingErrorStream();
}
/* (non-Javadoc) */
public boolean isNotRunning() {
return !isRunning();
}
/* (non-Javadoc) */
public boolean isRunning() {
return ProcessUtils.isRunning(process);
return ProcessUtils.isRunning(this.process);
}
/* (non-Javadoc) */
public File getWorkingDirectory() {
return processConfiguration.getWorkingDirectory();
return this.processConfiguration.getWorkingDirectory();
}
/* (non-Javadoc) */
public int exitValue() {
return process.exitValue();
return this.process.exitValue();
}
/* (non-Javadoc) */
public int safeExitValue() {
try {
return exitValue();
}
@@ -202,74 +194,75 @@ public class ProcessWrapper {
}
}
/* (non-Javadoc) */
public String readLogFile() throws IOException {
File[] logFiles = FileSystemUtils.listFiles(getWorkingDirectory(),
(path) -> (path != null && (path.isDirectory() || path.getAbsolutePath().endsWith(".log"))));
path -> (path != null && (path.isDirectory() || path.getAbsolutePath().endsWith(".log"))));
if (logFiles.length > 0) {
return readLogFile(logFiles[0]);
}
else {
throw new FileNotFoundException(String.format(
"No log files found in process's [%d] working directory [%s]",
safeGetPid(), getWorkingDirectory()));
throw new FileNotFoundException(String.format("No log files found in process's [%d] working directory [%s]",
safeGetPid(), getWorkingDirectory()));
}
}
/* (non-Javadoc) */
public String readLogFile(File log) throws IOException {
return FileUtils.read(log);
}
/* (non-Javadoc) */
public boolean register(ProcessInputStreamListener listener) {
return (listener != null && listeners.add(listener));
return listener != null && listeners.add(listener);
}
/* (non-Javadoc) */
public void registerShutdownHook() {
Runtime.getRuntime().addShutdownHook(new Thread(this::shutdown));
}
/* (non-Javadoc) */
public void signal() {
try {
OutputStream outputStream = process.getOutputStream();
OutputStream outputStream = this.process.getOutputStream();
outputStream.write("\n".getBytes());
outputStream.flush();
}
catch (IOException e) {
log.warning("Failed to signal process");
catch (IOException cause) {
if (log.isLoggable(Level.FINE)) {
log.fine(ThrowableUtils.toString(e));
this.log.warning("Failed to signal process");
if (this.log.isLoggable(Level.FINE)) {
this.log.fine(ThrowableUtils.toString(cause));
}
}
}
/* (non-Javadoc) */
public void signalStop() {
try {
ProcessUtils.signalStop(process);
}
catch (IOException e) {
log.warning("Failed to signal the process to stop");
if (log.isLoggable(Level.FINE)) {
log.fine(ThrowableUtils.toString(e));
try {
ProcessUtils.signalStop(this.process);
}
catch (IOException cause) {
this.log.warning("Failed to signal the process to stop");
if (this.log.isLoggable(Level.FINE)) {
this.log.fine(ThrowableUtils.toString(cause));
}
}
}
/* (non-Javadoc) */
public int stop() {
return stop(DEFAULT_WAIT_TIME_MILLISECONDS);
}
/* (non-Javadoc) */
public int stop(long milliseconds) {
if (isRunning()) {
boolean interrupted = false;
int exitValue = -1;
int pid = safeGetPid();
@@ -279,9 +272,10 @@ public class ProcessWrapper {
ExecutorService executorService = Executors.newSingleThreadExecutor();
try {
Future<Integer> futureExitValue = executorService.submit(() -> {
process.destroy();
int localExitValue = process.waitFor();
this.process.destroy();
int localExitValue = this.process.waitFor();
exited.set(true);
return localExitValue;
});
@@ -289,16 +283,18 @@ public class ProcessWrapper {
while (!exited.get() && System.currentTimeMillis() < timeout) {
try {
exitValue = futureExitValue.get(milliseconds, TimeUnit.MILLISECONDS);
log.info(String.format("Process [%s] has stopped%n", pid));
this.log.info(String.format("Process [%s] has stopped%n", pid));
}
catch (InterruptedException ignore) {
interrupted = true;
}
}
}
catch (TimeoutException e) {
catch (TimeoutException cause) {
exitValue = -1;
log.warning(String.format("Process [%1$d] did not stop within the allotted timeout of %2$d seconds%n",
this.log.warning(String.format("Process [%1$d] did not stop within the allotted timeout of %2$d seconds%n",
pid, TimeUnit.MILLISECONDS.toSeconds(milliseconds)));
}
catch (Exception ignore) {
@@ -319,10 +315,10 @@ public class ProcessWrapper {
}
}
/* (non-Javadoc) */
public int shutdown() {
if (isRunning()) {
log.info(String.format("Stopping process [%d]...%n", safeGetPid()));
this.log.info(String.format("Stopping process [%d]...%n", safeGetPid()));
signalStop();
waitFor();
}
@@ -330,17 +326,14 @@ public class ProcessWrapper {
return stop();
}
/* (non-Javadoc) */
public boolean unregister(ProcessInputStreamListener listener) {
return listeners.remove(listener);
return this.listeners.remove(listener);
}
/* (non-Javadoc) */
public void waitFor() {
waitFor(DEFAULT_WAIT_TIME_MILLISECONDS);
}
/* (non-Javadoc) */
public void waitFor(long milliseconds) {
ThreadUtils.timedWait(milliseconds, 500, this::isRunning);
}