quickstart.ProcessWrapper Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gemfire-junit Show documentation
Show all versions of gemfire-junit Show documentation
SnappyData store based off Pivotal GemFireXD
/*
* Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you
* may not use this file except in compliance with the License. You
* may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License. See accompanying
* LICENSE file.
*/
package quickstart;
import java.io.File;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Pattern;
import com.gemstone.gemfire.LogWriter;
import com.gemstone.gemfire.internal.LocalLogWriter;
import com.gemstone.gemfire.internal.LogWriterImpl;
import junit.framework.Assert;
/**
* Wraps spawned process to capture output and provide hooks to the
* {@link java.lang.Process} object.
*
* @author Kirk Lund
* @since 4.1.1
*/
public class ProcessWrapper extends Assert {
public static final boolean ENABLE_TRACING = Boolean.getBoolean("quickstart.test.ENABLE_TRACING");
protected static final String TIMEOUT_MILLIS_PROPERTY = "quickstart.test.TIMEOUT_MILLIS";
private static final long TIMEOUT_MILLIS = Long.getLong(TIMEOUT_MILLIS_PROPERTY, 5 * 60 * 1000L);
private static final boolean JAVA_AWT_HEADLESS = true;
private static final LogWriter logWriter = new LocalLogWriter(LogWriterImpl.INFO_LEVEL);
private final Class> mainClass;
private final String[] mainArgs;
private volatile Process process;
private volatile Throwable processException;
private volatile ProcessOutputReader outputReader;
private final boolean useMainLauncher;
private final List allLines;
private final BlockingQueue lineBuffer;
private final AtomicInteger exitValue = new AtomicInteger(-1);
private boolean starting = false;
private boolean started = false;
private boolean stopped = false;
private boolean interrupted = false;
private Thread processThread;
private ProcessStreamReader stdout;
private ProcessStreamReader stderr;
public ProcessWrapper(Class> main) {
this(main, null);
}
protected ProcessStreamReader getStandardOutReader() {
synchronized (this.exitValue) {
return stdout;
}
}
protected ProcessStreamReader getStandardErrorReader() {
synchronized (this.exitValue) {
return stderr;
}
}
private void waitForProcessStart() throws InterruptedException {
long start = System.currentTimeMillis();
boolean done = false;
while (!done) {
synchronized (this.exitValue) {
done = (this.process != null || this.processException != null) &&
(this.started || this.exitValue.get() > -1 || this.interrupted);
}
if (!done && System.currentTimeMillis() > start + TIMEOUT_MILLIS) {
fail("Timed out launching process");
}
Thread.sleep(100);
}
}
public boolean isAlive() throws InterruptedException {
checkStarting();
waitForProcessStart();
synchronized (this.exitValue) {
if (this.interrupted) { // TODO: do we want to do this?
throw new InterruptedException("Process was interrupted");
}
return this.exitValue.get() == -1 && this.started && !this.stopped && !this.interrupted && this.processThread.isAlive();
}
}
public ProcessWrapper destroy() {
if (this.process != null) {
this.process.destroy();
}
return this;
}
public ProcessWrapper(Class> main, String[] mainArgs) {
this(main, mainArgs, true);
}
public ProcessWrapper(Class> main, String[] mainArgs, boolean useMainLauncher) {
this.mainClass = main;
this.mainArgs = mainArgs;
this.lineBuffer = new LinkedBlockingQueue();
this.allLines = Collections.synchronizedList(new ArrayList());
this.useMainLauncher = useMainLauncher;
}
public int waitFor(long timeout, boolean throwOnTimeout) throws InterruptedException {
checkStarting();
Thread thread = getThread();
thread.join(timeout);
synchronized (this.exitValue) {
if (throwOnTimeout) {
checkStopped();
}
return this.exitValue.get();
}
}
public int waitFor(long timeout) throws InterruptedException {
return waitFor(timeout, false);
}
public int waitFor(boolean throwOnTimeout) throws InterruptedException {
return waitFor(TIMEOUT_MILLIS, throwOnTimeout);
}
public int waitFor() throws InterruptedException {
return waitFor(TIMEOUT_MILLIS, false);
}
public String getOutput() {
return getOutput(false);
}
public String getOutput(boolean ignoreStopped) {
checkStarting();
if (!ignoreStopped) {
checkStopped();
}
return this.outputReader.getOutput();
}
public ProcessWrapper sendInput() {
checkStarting();
sendInput("");
return this;
}
public ProcessWrapper sendInput(String input) {
checkStarting();
PrintStream ps = new PrintStream(this.process.getOutputStream());
ps.println(input);
ps.flush();
return this;
}
public ProcessWrapper failIfOutputMatches(String patternString, long timeoutMillis) throws InterruptedException {
checkStarting();
checkOk();
Pattern pattern = Pattern.compile(patternString);
trace("failIfOutputMatches waiting for \"" + patternString + "\"...");
long start = System.currentTimeMillis();
while(System.currentTimeMillis() <= start+timeoutMillis) {
String line = lineBuffer.poll(timeoutMillis, TimeUnit.MILLISECONDS);
if (line != null && pattern.matcher(line).matches()) {
fail("failIfOutputMatches Matched pattern \"" + patternString + "\" against output \"" + line + "\". Output: " + this.allLines);
}
}
return this;
}
/*
* Waits for the process stdout or stderr stream to contain the specified
* text. Uses the specified timeout for debugging purposes.
*/
public ProcessWrapper waitForOutputToMatch(String patternString, long timeoutMillis) throws InterruptedException {
checkStarting();
checkOk();
Pattern pattern = Pattern.compile(patternString);
trace("ProcessWrapper:waitForOutputToMatch waiting for \"" + patternString + "\"...");
while(true) {
String line = this.lineBuffer.poll(timeoutMillis, TimeUnit.MILLISECONDS);
if (line == null) {
fail("Timed out waiting for output \"" + patternString + "\" after " + TIMEOUT_MILLIS + " ms. Output: " + new OutputFormatter(this.allLines));
}
if (pattern.matcher(line).matches()) {
trace("ProcessWrapper:waitForOutputToMatch Matched pattern \"" + patternString + "\" against output \"" + line + "\"");
break;
} else {
trace("ProcessWrapper:waitForOutputToMatch Did not match pattern \"" + patternString + "\" against output \"" + line + "\"");
}
}
return this;
}
/*
* Waits for the process stdout or stderr stream to contain the specified
* text. Uses the default timeout.
*/
public ProcessWrapper waitForOutputToMatch(String patternString) throws InterruptedException {
return waitForOutputToMatch(patternString, TIMEOUT_MILLIS);
}
public ProcessWrapper execute() throws InterruptedException {
return execute(null, new File(System.getProperty("user.dir")));
}
public ProcessWrapper execute(Properties props) throws InterruptedException {
return execute(props, new File(System.getProperty("user.dir")));
}
public ProcessWrapper execute(final Properties props, final File workingDirectory) throws InterruptedException {
synchronized (this.exitValue) {
if (this.starting) {
throw new IllegalStateException("ProcessWrapper can only be executed once");
}
this.starting = true;
this.processThread = new Thread(new Runnable() {
public void run() {
exec(props, workingDirectory);
}
}, "ProcessWrapper Process Thread");
}
this.processThread.start();
waitForProcessStart();
synchronized (this.exitValue) {
if (this.processException != null) {
System.out.println("ProcessWrapper:execute failed with " + this.processException);
this.processException.printStackTrace();
}
}
if (this.useMainLauncher) {
sendInput(); // to trigger MainLauncher delegation to inner main
}
return this;
}
private void exec(Properties dsProps, final File workingDirectory) {
List vmArgList = new ArrayList();
if (dsProps != null) {
for (Map.Entry