ソースを参照

- Further simplification by using Futures :)

Sebastian Stenzel 10 年 前
コミット
b7f3f00ce2

+ 0 - 1
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;

+ 0 - 69
main/ui/src/main/java/org/cryptomator/ui/util/command/AsyncStreamCopier.java

@@ -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;
-		}
-	}
-
-}

+ 24 - 77
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()));
 		}
 	}
 

+ 12 - 3
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() {

+ 111 - 0
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<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();
+		}
+	}
+
+}