mirror of
https://github.com/cryptomator/cryptomator.git
synced 2025-02-01 07:52:17 +00:00
- Further simplification by using Futures :)
This commit is contained in:
parent
dbadf54893
commit
b7f3f00ce2
@ -22,7 +22,6 @@ import javafx.beans.value.ObservableValue;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.Initializable;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.ComboBox;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.ProgressIndicator;
|
||||
|
@ -1,69 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 Markus Kreusch
|
||||
* This file is licensed under the terms of the MIT license.
|
||||
* See the LICENSE.txt file for more info.
|
||||
*
|
||||
* Contributors:
|
||||
* Markus Kreusch
|
||||
******************************************************************************/
|
||||
package org.cryptomator.ui.util.command;
|
||||
|
||||
import static org.apache.commons.io.IOUtils.copy;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
final class AsyncStreamCopier implements Runnable {
|
||||
|
||||
private final Lock lock = new ReentrantLock();
|
||||
private final Condition doneCondition = lock.newCondition();
|
||||
private final InputStream input;
|
||||
private final OutputStream output;
|
||||
|
||||
private IOException exception;
|
||||
private boolean done;
|
||||
|
||||
public AsyncStreamCopier(InputStream input, OutputStream output) {
|
||||
this.input = input;
|
||||
this.output = output;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
lock.lock();
|
||||
try (InputStream inputToBeClosed = input; OutputStream outputToBeClosed = output) {
|
||||
copy(input, output);
|
||||
done = true;
|
||||
doneCondition.signal();
|
||||
} catch (IOException e) {
|
||||
exception = e;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private void waitUntilDone() {
|
||||
lock.lock();
|
||||
try {
|
||||
while (!done) {
|
||||
doneCondition.await();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void assertOk() throws IOException {
|
||||
this.waitUntilDone();
|
||||
if (exception != null) {
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -5,18 +5,16 @@
|
||||
*
|
||||
* Contributors:
|
||||
* Markus Kreusch
|
||||
* Sebastian Stenzel - using Futures, lazy loading for out/err.
|
||||
******************************************************************************/
|
||||
package org.cryptomator.ui.util.command;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static org.apache.commons.io.IOUtils.copy;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.cryptomator.ui.util.mount.CommandFailedException;
|
||||
import org.slf4j.Logger;
|
||||
@ -26,99 +24,48 @@ public class CommandResult {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(CommandResult.class);
|
||||
|
||||
private final ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
private final ByteArrayOutputStream error = new ByteArrayOutputStream();
|
||||
private final Lock lock = new ReentrantLock();
|
||||
private final Condition finishedCondition = lock.newCondition();
|
||||
private final Process process;
|
||||
|
||||
private final AsyncStreamCopier processOutputCopier;
|
||||
private final AsyncStreamCopier processErrorCopier;
|
||||
|
||||
private boolean finished;
|
||||
private CommandFailedException exception;
|
||||
|
||||
public CommandResult(Process process, String[] lines, Executor cmdExecutor, long timeout, TimeUnit unit) {
|
||||
this.process = process;
|
||||
processOutputCopier = new AsyncStreamCopier(process.getInputStream(), output);
|
||||
processErrorCopier = new AsyncStreamCopier(process.getErrorStream(), error);
|
||||
cmdExecutor.execute(processOutputCopier);
|
||||
cmdExecutor.execute(processErrorCopier);
|
||||
cmdExecutor.execute(new CommandTask(timeout, unit));
|
||||
public CommandResult(Process process) {
|
||||
this.process = process;
|
||||
}
|
||||
|
||||
public String getOutput() throws CommandFailedException {
|
||||
this.waitUntilFinished();
|
||||
return new String(output.toByteArray());
|
||||
try (InputStream in = process.getInputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream()) {
|
||||
copy(in, out);
|
||||
return new String(out.toByteArray());
|
||||
} catch (IOException e) {
|
||||
throw new CommandFailedException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public String getError() throws CommandFailedException {
|
||||
this.waitUntilFinished();
|
||||
return new String(error.toByteArray());
|
||||
try (InputStream in = process.getErrorStream(); ByteArrayOutputStream out = new ByteArrayOutputStream()) {
|
||||
copy(in, out);
|
||||
return new String(out.toByteArray());
|
||||
} catch (IOException e) {
|
||||
throw new CommandFailedException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public int getExitValue() throws CommandFailedException {
|
||||
this.waitUntilFinished();
|
||||
return process.exitValue();
|
||||
}
|
||||
|
||||
private void waitUntilFinished() throws CommandFailedException {
|
||||
lock.lock();
|
||||
try {
|
||||
while (!finished) {
|
||||
finishedCondition.await();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
if (exception != null) {
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
|
||||
private void logDebugInfo() {
|
||||
public void logDebugInfo() {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Command execution finished. Exit code: {}\n" + "Output:\n" + "{}\n" + "Error:\n" + "{}\n", process.exitValue(), new String(output.toByteArray()), new String(error.toByteArray()));
|
||||
try {
|
||||
LOG.debug("Command execution finished. Exit code: {}\n" + "Output:\n" + "{}\n" + "Error:\n" + "{}\n", process.exitValue(), getOutput(), getError());
|
||||
} catch (CommandFailedException e) {
|
||||
LOG.debug("Command execution finished. Exit code: {}\n", process.exitValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void assertOk() throws CommandFailedException {
|
||||
int exitValue = getExitValue();
|
||||
if (exitValue != 0) {
|
||||
throw new CommandFailedException(format("Command execution failed. Exit code: %d\n" + "# Output:\n" + "%s\n" + "# Error:\n" + "%s", exitValue, new String(output.toByteArray()),
|
||||
new String(error.toByteArray())));
|
||||
}
|
||||
}
|
||||
|
||||
private class CommandTask implements Runnable {
|
||||
|
||||
private final long timeout;
|
||||
private final TimeUnit unit;
|
||||
|
||||
private CommandTask(long timeout, TimeUnit unit) {
|
||||
this.timeout = timeout;
|
||||
this.unit = unit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
if (!process.waitFor(timeout, unit)) {
|
||||
exception = new CommandFailedException("Waiting time elapsed before command execution finished");
|
||||
}
|
||||
processOutputCopier.assertOk();
|
||||
processErrorCopier.assertOk();
|
||||
logDebugInfo();
|
||||
} catch (IOException | InterruptedException e) {
|
||||
exception = new CommandFailedException(e);
|
||||
} finally {
|
||||
lock.lock();
|
||||
finished = true;
|
||||
finishedCondition.signal();
|
||||
lock.unlock();
|
||||
}
|
||||
throw new CommandFailedException(format("Command execution failed. Exit code: %d\n" + "# Output:\n" + "%s\n" + "# Error:\n" + "%s", exitValue, getOutput(), getError()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,9 +14,11 @@ import static org.apache.commons.lang3.SystemUtils.IS_OS_WINDOWS;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
@ -62,7 +64,8 @@ final class CommandRunner {
|
||||
for (final String line : script.getLines()) {
|
||||
final String[] cmds = ArrayUtils.add(determineCli(), line);
|
||||
final Process proc = Runtime.getRuntime().exec(cmds, env.toArray(new String[0]));
|
||||
result = run(proc, script.getLines(), timeout, unit);
|
||||
result = run(proc, timeout, unit);
|
||||
result.logDebugInfo();
|
||||
result.assertOk();
|
||||
}
|
||||
return result;
|
||||
@ -71,8 +74,14 @@ final class CommandRunner {
|
||||
}
|
||||
}
|
||||
|
||||
private static CommandResult run(Process process, String[] lines, long timeout, TimeUnit unit) {
|
||||
return new CommandResult(process, lines, CMD_EXECUTOR, timeout, unit);
|
||||
private static CommandResult run(Process process, long timeout, TimeUnit unit) throws CommandFailedException {
|
||||
try {
|
||||
final FutureCommandResult futureCommandResult = new FutureCommandResult(process);
|
||||
CMD_EXECUTOR.execute(futureCommandResult);
|
||||
return futureCommandResult.get(timeout, unit);
|
||||
} catch (InterruptedException | ExecutionException | TimeoutException e) {
|
||||
throw new CommandFailedException("Waiting time elapsed before command execution finished");
|
||||
}
|
||||
}
|
||||
|
||||
private static String[] determineCli() {
|
||||
|
@ -0,0 +1,111 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 Markus Kreusch
|
||||
* This file is licensed under the terms of the MIT license.
|
||||
* See the LICENSE.txt file for more info.
|
||||
*
|
||||
* Contributors:
|
||||
* Sebastian Stenzel
|
||||
******************************************************************************/
|
||||
package org.cryptomator.ui.util.command;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import org.cryptomator.ui.util.mount.CommandFailedException;
|
||||
|
||||
class FutureCommandResult implements Future<CommandResult>, Runnable {
|
||||
|
||||
private final Process process;
|
||||
private final AtomicBoolean canceled = new AtomicBoolean();
|
||||
private final AtomicBoolean done = new AtomicBoolean();
|
||||
private final Lock lock = new ReentrantLock();
|
||||
private final Condition doneCondition = lock.newCondition();
|
||||
|
||||
private CommandFailedException exception;
|
||||
|
||||
public FutureCommandResult(Process process) {
|
||||
this.process = process;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cancel(boolean mayInterruptIfRunning) {
|
||||
if (done.get()) {
|
||||
return false;
|
||||
} else if (canceled.compareAndSet(false, true)) {
|
||||
if (mayInterruptIfRunning) {
|
||||
process.destroyForcibly();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return canceled.get();
|
||||
}
|
||||
|
||||
private void setDone() {
|
||||
lock.lock();
|
||||
try {
|
||||
done.set(true);
|
||||
doneCondition.signalAll();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDone() {
|
||||
return done.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandResult get() throws InterruptedException, ExecutionException {
|
||||
lock.lock();
|
||||
try {
|
||||
while(!done.get()) {
|
||||
doneCondition.await();
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
if (exception != null) {
|
||||
throw new ExecutionException(exception);
|
||||
}
|
||||
return new CommandResult(process);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandResult get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
|
||||
lock.lock();
|
||||
try {
|
||||
while(!done.get()) {
|
||||
doneCondition.await(timeout, unit);
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
if (exception != null) {
|
||||
throw new ExecutionException(exception);
|
||||
}
|
||||
return new CommandResult(process);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
process.waitFor();
|
||||
} catch (InterruptedException e) {
|
||||
exception = new CommandFailedException(e);
|
||||
} finally {
|
||||
setDone();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user