diff --git a/main/ui/src/main/java/org/cryptomator/ui/UnlockController.java b/main/ui/src/main/java/org/cryptomator/ui/UnlockController.java index 613920764..ad629ad85 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/UnlockController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/UnlockController.java @@ -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; diff --git a/main/ui/src/main/java/org/cryptomator/ui/util/command/AsyncStreamCopier.java b/main/ui/src/main/java/org/cryptomator/ui/util/command/AsyncStreamCopier.java deleted file mode 100644 index da98bd9a7..000000000 --- a/main/ui/src/main/java/org/cryptomator/ui/util/command/AsyncStreamCopier.java +++ /dev/null @@ -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; - } - } - -} diff --git a/main/ui/src/main/java/org/cryptomator/ui/util/command/CommandResult.java b/main/ui/src/main/java/org/cryptomator/ui/util/command/CommandResult.java index d11ccd67e..695033e0f 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/util/command/CommandResult.java +++ b/main/ui/src/main/java/org/cryptomator/ui/util/command/CommandResult.java @@ -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())); } } diff --git a/main/ui/src/main/java/org/cryptomator/ui/util/command/CommandRunner.java b/main/ui/src/main/java/org/cryptomator/ui/util/command/CommandRunner.java index c2e9c2ce7..f2b568936 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/util/command/CommandRunner.java +++ b/main/ui/src/main/java/org/cryptomator/ui/util/command/CommandRunner.java @@ -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() { diff --git a/main/ui/src/main/java/org/cryptomator/ui/util/command/FutureCommandResult.java b/main/ui/src/main/java/org/cryptomator/ui/util/command/FutureCommandResult.java new file mode 100644 index 000000000..3e38b5c83 --- /dev/null +++ b/main/ui/src/main/java/org/cryptomator/ui/util/command/FutureCommandResult.java @@ -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, 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(); + } + } + +}