package com.github.davidmoten.rx;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import com.github.davidmoten.util.Optional;
import rx.Observable;
import rx.Observable.OnSubscribe;
import rx.Subscriber;
import rx.functions.Action1;
import rx.functions.Func0;
import rx.functions.Func1;
import rx.schedulers.Schedulers;
public final class Processes {
public static void main(String[] args) throws IOException, InterruptedException {
execute("ls").map(new Func1<byte[], String>() {
@Override
public String call(byte[] bytes) {
return new String(bytes);
}
});
}
public static Observable<byte[]> execute(String... command) {
return execute(
new Parameters(Arrays.asList(command), Optional.<Map<String, String>> absent(),
true, new File("."), Optional.<Long> absent()));
}
public static Observable<byte[]> execute(final Parameters parameters) {
Func0<Process> resourceFactory = new Func0<Process>() {
@Override
public Process call() {
ProcessBuilder b = new ProcessBuilder(parameters.command());
if (parameters.env().isPresent()) {
if (parameters.appendEnv())
b.environment().clear();
b.environment().putAll(parameters.env().get());
}
b.directory(parameters.directory());
b.redirectErrorStream(true);
try {
return b.start();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
};
Func1<Process, Observable<byte[]>> factory = new Func1<Process, Observable<byte[]>>() {
@Override
public Observable<byte[]> call(final Process process) {
InputStream is = process.getInputStream();
Observable<byte[]> output;
if (is != null)
output = Bytes.from(is);
else
output = Observable.empty();
Observable<byte[]> completion = Observable.create(new OnSubscribe<byte[]>() {
@Override
public void call(Subscriber<? super byte[]> sub) {
try {
// TODO waitFor does not exist pre 1.8 with timeout!
// parameters.waitForMs().get(),TimeUnit.MILLISECONDS);
if (parameters.waitForMs().isPresent()) {
// boolean finished = process.waitFor(
// parameters.waitForMs().get(),
// TimeUnit.MILLISECONDS);
// if (!finished) {
// sub.onError(new TimeoutException("process
// timed out"));
// return;
// }
sub.onError(new IllegalArgumentException("not implemented yet"));
} else {
int exitCode = process.waitFor();
if (exitCode != 0)
sub.onError(new ProcessException(exitCode));
return;
}
sub.onCompleted();
} catch (InterruptedException e) {
sub.onError(e);
}
}
}).subscribeOn(Schedulers.io());
return output.concatWith(completion);
}
};
Action1<? super Process> disposeAction = new Action1<Process>() {
@Override
public void call(Process process) {
process.destroy();
}
};
return Observable.using(resourceFactory, factory, disposeAction);
}
public static class ProcessException extends RuntimeException {
private static final long serialVersionUID = 722422557667123473L;
private final int exitCode;
public ProcessException(int exitCode) {
super("process returned exitCode " + exitCode);
this.exitCode = exitCode;
}
public int exitCode() {
return exitCode;
}
}
public static final class Parameters {
private final List<String> command;
private final Optional<Map<String, String>> env;
private final boolean appendEnv;
private final File directory;
private final Optional<Long> waitForMs;
public Parameters(List<String> command, Optional<Map<String, String>> env,
boolean appendEnv, File directory, Optional<Long> waitForMs) {
this.command = command;
this.env = env;
this.appendEnv = appendEnv;
this.directory = directory;
this.waitForMs = waitForMs;
}
public Optional<Long> waitForMs() {
return waitForMs;
}
public File directory() {
return directory;
}
public List<String> command() {
return command;
}
public Optional<Map<String, String>> env() {
return env;
}
public boolean appendEnv() {
return appendEnv;
}
}
}