diff --git a/build.gradle b/build.gradle index 5bb9027235..8daacc38d4 100644 --- a/build.gradle +++ b/build.gradle @@ -191,9 +191,6 @@ project(":ui") { compile project(path: ':core', configuration: 'shadow') ideProvider project(path: ':core', configuration: 'compile') compile group: 'org.controlsfx', name: 'controlsfx', version: '8.40.10' - compile group: 'org.apache.ant', name: 'ant-jsch', version: '1.8.1' - compile group: 'com.jcabi', name: 'jcabi-ssh', version: '1.5' - compile group: 'org.jdeferred', name: 'jdeferred-core', version: '1.2.4' testCompile files(project(':core').sourceSets.test.output.classesDir) testCompile files(project(':core').sourceSets.test.output.resourcesDir) testCompile group: 'org.testfx', name: 'testfx-core', version: '4.0.+' diff --git a/core/src/main/java/edu/wpi/grip/core/Main.java b/core/src/main/java/edu/wpi/grip/core/Main.java index 448b951874..260dd2b5fa 100644 --- a/core/src/main/java/edu/wpi/grip/core/Main.java +++ b/core/src/main/java/edu/wpi/grip/core/Main.java @@ -26,12 +26,12 @@ public class Main { @Inject private Logger logger; - public static void main(String[] args) throws IOException, InterruptedException { + @SuppressWarnings("PMD.SignatureDeclareThrowsException") + public static void main(String[] args) throws Exception { final Injector injector = Guice.createInjector(new GRIPCoreModule()); injector.getInstance(Main.class).start(args); } - @SuppressWarnings("PMD.SystemPrintln") public void start(String[] args) throws IOException, InterruptedException { if (args.length != 1) { System.err.println("Usage: GRIP.jar project.grip"); @@ -69,9 +69,6 @@ public void start(String[] args) throws IOException, InterruptedException { // Open a project from a .grip file specified on the command line project.open(new File(projectPath)); - - // This is done in order to indicate to the user using the deployment UI that this is running - System.out.println("SUCCESS! The project is running in headless mode!"); // There's nothing more to do in the main thread since we're in headless mode - sleep forever for (; ; ) { Thread.sleep(Integer.MAX_VALUE); diff --git a/ui/src/main/java/edu/wpi/grip/ui/DeployerController.java b/ui/src/main/java/edu/wpi/grip/ui/DeployerController.java deleted file mode 100644 index 3bc3c1dd0e..0000000000 --- a/ui/src/main/java/edu/wpi/grip/ui/DeployerController.java +++ /dev/null @@ -1,130 +0,0 @@ -package edu.wpi.grip.ui; - - -import com.google.common.base.Throwables; -import com.google.inject.Inject; -import edu.wpi.grip.ui.annotations.ParametrizedController; -import edu.wpi.grip.ui.components.StartStoppableButton; -import edu.wpi.grip.ui.deployment.DeploymentOptionsController; -import edu.wpi.grip.ui.deployment.DeploymentOptionsControllersFactory; -import edu.wpi.grip.ui.util.deployment.DeployedInstanceManager; -import javafx.application.Platform; -import javafx.fxml.FXML; -import javafx.scene.control.Accordion; -import javafx.scene.control.DialogPane; -import javafx.scene.control.ProgressBar; -import javafx.scene.control.TextArea; -import javafx.scene.layout.HBox; - -import java.io.IOException; -import java.io.OutputStream; -import java.io.PrintStream; -import java.util.function.Supplier; -import java.util.stream.Collectors; - -@ParametrizedController(url = "DeployerPane.fxml") -public class DeployerController { - - @FXML - private DialogPane root; - - @FXML - private HBox controlsBox; - - @FXML - private Accordion deploymentMethods; - - @FXML - private TextArea stdOutStreamTextArea; - - @FXML - private TextArea stdErrStreamTextArea; - - @FXML - private ProgressBar progressIndicator; - - private final StartStoppableButton.Factory startStopButtonFactory; - private final DeploymentOptionsControllersFactory optionsControllersFactory; - - private class StreamToTextArea extends OutputStream { - private final TextArea outputArea; - - public StreamToTextArea(TextArea outputArea) { - super(); - this.outputArea = outputArea; - } - - public StreamToTextArea reset() { - outputArea.clear(); - return this; - } - - @Override - public void write(int i) throws IOException { - outputArea.appendText(String.valueOf((char) i)); - } - } - - - public interface Factory { - DeployerController create(); - } - - @Inject - DeployerController(StartStoppableButton.Factory startStopButtonFactory, DeploymentOptionsControllersFactory optionsControllersFactory) { - this.startStopButtonFactory = startStopButtonFactory; - this.optionsControllersFactory = optionsControllersFactory; - } - - @FXML - @SuppressWarnings("PMD.UnusedPrivateMethod") - private void initialize() { - final Supplier out = () -> - new PrintStream(new StreamToTextArea(stdOutStreamTextArea).reset(), false); - final Supplier err = () -> - new PrintStream(new StreamToTextArea(stdErrStreamTextArea).reset(), false); - deploymentMethods.getPanes().addAll( - optionsControllersFactory - .createControllers(this::onDeploy, out, err) - .stream() - .map(DeploymentOptionsController::getRoot) - .collect(Collectors.toList())); - } - - /** - * Calls {@link DeployedInstanceManager#deploy()} and displays the result to the UI. - * @param manager The manager to call deploy on - */ - private void onDeploy(DeployedInstanceManager manager) { - Platform.runLater(() -> { - progressIndicator.setProgress(0); - deploymentMethods.setDisable(true); - }); - manager.deploy() - .fail(throwable -> { - Platform.runLater(() -> { - stdErrStreamTextArea.setText("Failed to deploy\n" + - Throwables.getStackTraceAsString(throwable) - ); - deploymentMethods.setDisable(false); - }); - }) - .progress(percent -> { - Platform.runLater(() -> progressIndicator.setProgress(percent)); - }) - .done(deployedManager -> { - Platform.runLater(() -> { - controlsBox.getChildren().add(startStopButtonFactory.create(deployedManager)); - deploymentMethods.setDisable(true); - progressIndicator.setProgress(-1); - }); - }); - - } - - public DialogPane getRoot() { - return root; - } -} - - diff --git a/ui/src/main/java/edu/wpi/grip/ui/GRIPUIModule.java b/ui/src/main/java/edu/wpi/grip/ui/GRIPUIModule.java index 2b3df008a5..120fa4b564 100644 --- a/ui/src/main/java/edu/wpi/grip/ui/GRIPUIModule.java +++ b/ui/src/main/java/edu/wpi/grip/ui/GRIPUIModule.java @@ -10,8 +10,6 @@ import edu.wpi.grip.core.Source; import edu.wpi.grip.ui.annotations.ParametrizedController; import edu.wpi.grip.ui.components.StartStoppableButton; -import edu.wpi.grip.ui.deployment.FRCAdvancedDeploymentOptionsController; -import edu.wpi.grip.ui.deployment.FRCDeploymentOptionsController; import edu.wpi.grip.ui.pipeline.OutputSocketController; import edu.wpi.grip.ui.pipeline.SocketHandleView; import edu.wpi.grip.ui.pipeline.StepController; @@ -70,9 +68,6 @@ public void hear(final TypeLiteral typeLiteral, TypeEncounter typeEnco install(new FactoryModuleBuilder().build(OperationController.Factory.class)); install(new FactoryModuleBuilder().build(SocketHandleView.Factory.class)); install(new FactoryModuleBuilder().build(OutputSocketController.Factory.class)); - install(new FactoryModuleBuilder().build(DeployerController.Factory.class)); - install(new FactoryModuleBuilder().build(FRCDeploymentOptionsController.Factory.class)); - install(new FactoryModuleBuilder().build(FRCAdvancedDeploymentOptionsController.Factory.class)); // End arbitrary controllers // InputSocketController Factories diff --git a/ui/src/main/java/edu/wpi/grip/ui/MainWindowController.java b/ui/src/main/java/edu/wpi/grip/ui/MainWindowController.java index 44c3c04813..c5ad87d8b4 100644 --- a/ui/src/main/java/edu/wpi/grip/ui/MainWindowController.java +++ b/ui/src/main/java/edu/wpi/grip/ui/MainWindowController.java @@ -7,7 +7,6 @@ import javafx.application.Platform; import javafx.fxml.FXML; import javafx.scene.Parent; -import javafx.scene.control.Alert; import javafx.scene.control.ButtonType; import javafx.scene.control.Dialog; import javafx.scene.control.SplitPane; @@ -40,8 +39,6 @@ public class MainWindowController { private Palette palette; @Inject private Project project; - @Inject - private DeployerController.Factory deployerControllerFactoy; public void initialize() { pipelineView.prefHeightProperty().bind(bottomPane.heightProperty()); @@ -167,21 +164,5 @@ public void quit() { Platform.exit(); } } - - @FXML - public void deployFRC() { - if (project.getFile().isPresent()) { - final DeployerController deployerController = deployerControllerFactoy.create(); - final Dialog dialog = new Dialog(); - dialog.setDialogPane(deployerController.getRoot()); - dialog.setResizable(true); - dialog.showAndWait(); - } else { - final Alert alert = new Alert(Alert.AlertType.INFORMATION, - "You must have saved your project before it can be deployed to a remote device."); - alert.showAndWait(); - } - - } } diff --git a/ui/src/main/java/edu/wpi/grip/ui/deployment/DeploymentOptionsController.java b/ui/src/main/java/edu/wpi/grip/ui/deployment/DeploymentOptionsController.java deleted file mode 100644 index d597193db3..0000000000 --- a/ui/src/main/java/edu/wpi/grip/ui/deployment/DeploymentOptionsController.java +++ /dev/null @@ -1,132 +0,0 @@ -package edu.wpi.grip.ui.deployment; - -import edu.wpi.grip.ui.annotations.ParametrizedController; -import edu.wpi.grip.ui.util.SupplierWithIO; -import edu.wpi.grip.ui.util.deployment.DeployedInstanceManager; -import javafx.application.Platform; -import javafx.fxml.FXML; -import javafx.scene.control.Button; -import javafx.scene.control.Label; -import javafx.scene.control.ProgressIndicator; -import javafx.scene.control.TitledPane; -import javafx.scene.layout.GridPane; -import org.jdeferred.DeferredCallable; -import org.jdeferred.DeferredManager; -import org.jdeferred.Promise; -import org.jdeferred.impl.DefaultDeferredManager; - -import java.io.IOException; -import java.net.InetAddress; -import java.util.function.Consumer; - -/** - * A simple UI component that can be used to display options for deploying to a remote device using - * asynchronous callbacks. - */ -@ParametrizedController(url = "DeploymentOptions.fxml") -public abstract class DeploymentOptionsController { - - @FXML - private TitledPane root; - - @FXML - private GridPane optionsGrid; - - @FXML - private Label deployErrorText; - - @FXML - private ProgressIndicator deploySpinner; - - @FXML - private Button deployButton; - - private final String title; - private final Consumer onDeployCallback; - - DeploymentOptionsController(String title, Consumer onDeployCallback) { - this.title = title; - this.onDeployCallback = onDeployCallback; - } - - @FXML - protected final void initialize() { - root.setText(title); - postInit(); - } - - /** - * Called after the initialize method - */ - protected abstract void postInit(); - - protected abstract Promise onDeploy(); - - @FXML - @SuppressWarnings("PMD.UnusedPrivateMethod") - private void deploy() { - deploySpinner.setVisible(true); - deployButton.setDisable(true); - onDeploy() - .progress(this::setErrorText) - .fail((text) -> { - setErrorText(text); - Platform.runLater(() -> { - deploySpinner.setVisible(false); - deployButton.setDisable(false); - }); - }) - .done((t) -> { - onDeployCallback.accept(t); - Platform.runLater(() -> { - deploySpinner.setVisible(false); - deployButton.setDisable(false); - }); - }); - } - - private void setErrorText(String text) { - Platform.runLater(() -> { - deployErrorText.setText(text); - root.requestLayout(); - }); - } - - protected GridPane getOptionsGrid() { - return optionsGrid; - } - - protected Button getDeployButton() { - return deployButton; - } - - public TitledPane getRoot() { - return root; - } - - /** - * Checks that an InetAddress is reachable asynchronously - * - * @param address The address supplier to check - * @return A promise that is resolved when the InetAddress is determined to be reachable. - */ - protected static Promise checkInetAddressReachable(SupplierWithIO address) { - final DeferredManager checkAddressDeferred = new DefaultDeferredManager(); - return checkAddressDeferred.when(new DeferredCallable() { - @Override - public InetAddress call() throws IOException { - final InetAddress inetAddress = address.getWithIO(); - final int attemptCount = 5; - for (int i = 0; i < attemptCount; i++) { - if (inetAddress.isReachable(1000)) { - return inetAddress; - } else { - notify("Attempt " + i + "/" + attemptCount + " failed"); - } - } - throw new IOException("Failed to connect"); - } - }); - - } -} diff --git a/ui/src/main/java/edu/wpi/grip/ui/deployment/DeploymentOptionsControllersFactory.java b/ui/src/main/java/edu/wpi/grip/ui/deployment/DeploymentOptionsControllersFactory.java deleted file mode 100644 index 3345493a2b..0000000000 --- a/ui/src/main/java/edu/wpi/grip/ui/deployment/DeploymentOptionsControllersFactory.java +++ /dev/null @@ -1,35 +0,0 @@ -package edu.wpi.grip.ui.deployment; - - -import com.google.inject.Inject; -import com.google.inject.Singleton; -import edu.wpi.grip.ui.util.deployment.DeployedInstanceManager; - -import java.io.OutputStream; -import java.util.Arrays; -import java.util.Collection; -import java.util.function.Consumer; -import java.util.function.Supplier; - -@Singleton -public class DeploymentOptionsControllersFactory { - - private final FRCDeploymentOptionsController.Factory frcDeploymentOptionsControllerFactory; - private final FRCAdvancedDeploymentOptionsController.Factory frcAdvancedDeploymentOptionsControllerFactory; - - @Inject - DeploymentOptionsControllersFactory( - FRCDeploymentOptionsController.Factory frcDeploymentOptionsControllerFactory, - FRCAdvancedDeploymentOptionsController.Factory frcAdvancedDeploymentOptionsControllerFactory) { - this.frcDeploymentOptionsControllerFactory = frcDeploymentOptionsControllerFactory; - this.frcAdvancedDeploymentOptionsControllerFactory = frcAdvancedDeploymentOptionsControllerFactory; - } - - - public Collection createControllers(Consumer onDeployCallback, Supplier stdOut, Supplier stdErr) { - return Arrays.asList( - frcDeploymentOptionsControllerFactory.create(onDeployCallback, stdOut, stdErr), - frcAdvancedDeploymentOptionsControllerFactory.create(onDeployCallback, stdOut, stdErr) - ); - } -} diff --git a/ui/src/main/java/edu/wpi/grip/ui/deployment/FRCAdvancedDeploymentOptionsController.java b/ui/src/main/java/edu/wpi/grip/ui/deployment/FRCAdvancedDeploymentOptionsController.java deleted file mode 100644 index bd097d9279..0000000000 --- a/ui/src/main/java/edu/wpi/grip/ui/deployment/FRCAdvancedDeploymentOptionsController.java +++ /dev/null @@ -1,76 +0,0 @@ -package edu.wpi.grip.ui.deployment; - -import com.google.inject.Inject; -import com.google.inject.assistedinject.Assisted; -import edu.wpi.grip.ui.util.deployment.DeployedInstanceManager; -import javafx.scene.control.Label; -import javafx.scene.control.TextField; -import org.jdeferred.Deferred; -import org.jdeferred.Promise; -import org.jdeferred.impl.DeferredObject; - -import java.io.OutputStream; -import java.net.InetAddress; -import java.util.function.Consumer; -import java.util.function.Supplier; - -public class FRCAdvancedDeploymentOptionsController extends DeploymentOptionsController { - - - private final DeployedInstanceManager.Factory deployedInstanceManagerFactor; - private final Supplier stdOut, stdErr; - - private TextField address; - - public interface Factory { - FRCAdvancedDeploymentOptionsController create( - Consumer onDeployCallback, - @Assisted("stdOut") Supplier stdOut, - @Assisted("stdErr") Supplier stdErr - ); - } - - @Inject - FRCAdvancedDeploymentOptionsController(DeployedInstanceManager.Factory deployedInstanceManagerFactor, - @Assisted Consumer onDeployCallback, - @Assisted("stdOut") Supplier stdOut, - @Assisted("stdErr") Supplier stdErr) { - super("FRC Advanced", onDeployCallback); - this.deployedInstanceManagerFactor = deployedInstanceManagerFactor; - this.stdOut = stdOut; - this.stdErr = stdErr; - - } - - @Override - protected void postInit() { - this.address = new TextField(); - this.address.setPromptText("roborio-[team number]-frc.local"); - - this.address.textProperty().addListener((observable, oldValue, newValue) -> { - // Enable the "Deploy" button only if the user has entered something. - // Note: InetAddresses.isInetAddress only works for IP address not mdns names - if ("".equals(newValue)) { - getDeployButton().setDisable(true); - } else { - getDeployButton().setDisable(false); - } - }); - getDeployButton().setDisable(true); - - final Label textFieldInput = new Label("Address/IP:"); - textFieldInput.setLabelFor(this.address); - - getOptionsGrid().addRow(0, textFieldInput, this.address); - } - - @Override - protected Promise onDeploy() { - final Deferred deploy = new DeferredObject<>(); - checkInetAddressReachable(() -> InetAddress.getByName(address.getText())) - .progress(deploy::notify) - .fail(throwable -> deploy.reject(throwable.getMessage())) - .done((address) -> deploy.resolve(deployedInstanceManagerFactor.createFRC(address, stdOut, stdErr))); - return deploy.promise(); - } -} diff --git a/ui/src/main/java/edu/wpi/grip/ui/deployment/FRCDeploymentOptionsController.java b/ui/src/main/java/edu/wpi/grip/ui/deployment/FRCDeploymentOptionsController.java deleted file mode 100644 index 1c465e02b4..0000000000 --- a/ui/src/main/java/edu/wpi/grip/ui/deployment/FRCDeploymentOptionsController.java +++ /dev/null @@ -1,76 +0,0 @@ -package edu.wpi.grip.ui.deployment; - -import com.google.inject.Inject; -import com.google.inject.assistedinject.Assisted; -import edu.wpi.grip.ui.util.deployment.DeployedInstanceManager; -import javafx.scene.control.Label; -import javafx.scene.control.Spinner; -import javafx.scene.control.SpinnerValueFactory; -import org.jdeferred.Deferred; -import org.jdeferred.Promise; -import org.jdeferred.impl.DeferredObject; - -import java.io.OutputStream; -import java.net.InetAddress; -import java.util.function.Consumer; -import java.util.function.Supplier; - -import static com.google.common.base.Preconditions.checkNotNull; - -public class FRCDeploymentOptionsController extends DeploymentOptionsController { - - private Spinner teamNumberSpinner; - private final DeployedInstanceManager.Factory deployedInstanceManagerFactor; - private final Supplier stdOut, stdErr; - - public interface Factory { - FRCDeploymentOptionsController create( - Consumer onDeployCallback, - @Assisted("stdOut") Supplier stdOut, - @Assisted("stdErr") Supplier stdErr - ); - } - - @Inject - FRCDeploymentOptionsController(DeployedInstanceManager.Factory deployedInstanceManagerFactor, - @Assisted Consumer onDeployCallback, - @Assisted("stdOut") Supplier stdOut, - @Assisted("stdErr") Supplier stdErr) { - super("FRC", onDeployCallback); - this.deployedInstanceManagerFactor = deployedInstanceManagerFactor; - this.stdOut = stdOut; - this.stdErr = stdErr; - } - - @Override - @SuppressWarnings("PMD.IfElseStmtsMustUseBraces") - protected void postInit() { - final Label label = new Label("Team Number"); - final SpinnerValueFactory.IntegerSpinnerValueFactory spinnerValueFactory = new SpinnerValueFactory.IntegerSpinnerValueFactory(0, Integer.MAX_VALUE, 190); - this.teamNumberSpinner = new Spinner(spinnerValueFactory); - this.teamNumberSpinner.setEditable(true); - // Ensure the value entered is only a number - this.teamNumberSpinner.getEditor().textProperty().addListener((observable, oldValue, newValue) -> { - if ("".equals(newValue)) { - teamNumberSpinner.getEditor().setText(Integer.toString(0)); - } else try { - int value = Integer.parseInt(newValue); - teamNumberSpinner.getEditor().setText(Integer.toString(value)); - } catch (NumberFormatException e) { - teamNumberSpinner.getEditor().setText(oldValue); - } - }); - getOptionsGrid().addRow(0, label, this.teamNumberSpinner); - } - - @Override - protected Promise onDeploy() { - final Deferred deferredDeploy = new DeferredObject<>(); - int teamNumber = checkNotNull(teamNumberSpinner.getValue(), "Team number can not be null"); - checkInetAddressReachable(() -> InetAddress.getByName("roborio-" + teamNumber + "-frc.local")) - .progress(deferredDeploy::notify) - .fail(throwable -> deferredDeploy.reject(throwable.getMessage())) - .done(address -> deferredDeploy.resolve(deployedInstanceManagerFactor.createFRC(address, stdOut, stdErr))); - return deferredDeploy.promise(); - } -} diff --git a/ui/src/main/java/edu/wpi/grip/ui/pipeline/AddSourceView.java b/ui/src/main/java/edu/wpi/grip/ui/pipeline/AddSourceView.java index 3c04626ea5..83703a1818 100644 --- a/ui/src/main/java/edu/wpi/grip/ui/pipeline/AddSourceView.java +++ b/ui/src/main/java/edu/wpi/grip/ui/pipeline/AddSourceView.java @@ -9,7 +9,6 @@ import edu.wpi.grip.core.sources.ImageFileSource; import edu.wpi.grip.core.sources.MultiImageFileSource; import edu.wpi.grip.ui.util.DPIUtility; -import edu.wpi.grip.ui.util.SupplierWithIO; import javafx.application.Platform; import javafx.event.EventHandler; import javafx.scene.Parent; @@ -51,6 +50,11 @@ public class AddSourceView extends HBox { private final Button ipcamButton; private Optional activeDialog = Optional.empty(); + @FunctionalInterface + private interface SupplierWithIO { + T getWithIO() throws IOException; + } + private static class SourceDialog extends Dialog { private final Text errorText = new Text(); diff --git a/ui/src/main/java/edu/wpi/grip/ui/util/SupplierWithIO.java b/ui/src/main/java/edu/wpi/grip/ui/util/SupplierWithIO.java deleted file mode 100644 index f3c879bd0d..0000000000 --- a/ui/src/main/java/edu/wpi/grip/ui/util/SupplierWithIO.java +++ /dev/null @@ -1,14 +0,0 @@ -package edu.wpi.grip.ui.util; - -import java.io.IOException; - - -/** - * A supplier that can throw an IO exception. Thus putting it on the caller to handle it instead of on - * the creator of the lambda function. - * @param The type that the supplier returns. - */ -@FunctionalInterface -public interface SupplierWithIO { - T getWithIO() throws IOException; -} diff --git a/ui/src/main/java/edu/wpi/grip/ui/util/deployment/DeployedInstanceManager.java b/ui/src/main/java/edu/wpi/grip/ui/util/deployment/DeployedInstanceManager.java deleted file mode 100644 index 488b3602d8..0000000000 --- a/ui/src/main/java/edu/wpi/grip/ui/util/deployment/DeployedInstanceManager.java +++ /dev/null @@ -1,237 +0,0 @@ -package edu.wpi.grip.ui.util.deployment; - - -import com.google.common.eventbus.EventBus; -import com.google.inject.Inject; -import com.google.inject.Singleton; -import com.jcabi.ssh.Shell; -import com.jcraft.jsch.JSch; -import edu.wpi.grip.core.StartStoppable; -import edu.wpi.grip.core.events.StartedStoppedEvent; -import edu.wpi.grip.core.events.UnexpectedThrowableEvent; -import edu.wpi.grip.core.serialization.Project; -import org.apache.commons.io.input.NullInputStream; -import org.apache.commons.io.output.NullOutputStream; -import org.apache.tools.ant.BuildException; -import org.apache.tools.ant.taskdefs.optional.ssh.Scp; -import org.jdeferred.DeferredCallable; -import org.jdeferred.DeferredManager; -import org.jdeferred.Promise; -import org.jdeferred.impl.DefaultDeferredManager; - -import java.io.File; -import java.io.IOException; -import java.io.OutputStream; -import java.net.InetAddress; -import java.net.URISyntaxException; -import java.net.URLDecoder; -import java.nio.file.Paths; -import java.util.Optional; -import java.util.function.Supplier; -import java.util.logging.Level; -import java.util.logging.Logger; - -import static com.google.common.base.Preconditions.checkNotNull; - -/** - * Controls an instance of GRIP running on a remote device. - */ -public class DeployedInstanceManager implements StartStoppable { - - private final Logger logger = Logger.getLogger(DeployedInstanceManager.class.getName()); - private final EventBus eventBus; - private final File coreJar; - private final File projectFile; - private final SecureShellDetails details; - private final DeploymentCommands deploymentCommands; - private final Supplier stdOut; - private final Supplier stdErr; - private Optional sshThread; - - @Singleton - public static class Factory { - private final EventBus eventBus; - private final File coreJAR; - private final Project project; - private final SecureShellDetails.Factory secureShellDetailsFactory; - private final DeploymentCommands.Factory deploymentCommandsFactory; - - @Inject - public Factory( - EventBus eventBus, - Project project, - SecureShellDetails.Factory secureShellDetailsFactory, - DeploymentCommands.Factory deploymentCommandsFactory) { - this.eventBus = eventBus; - try { - this.coreJAR = new File(edu.wpi.grip.core.Main.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath()); - } catch (URISyntaxException e) { - throw new IllegalStateException("Could not find the main class jar file", e); - } - this.project = project; - this.secureShellDetailsFactory = secureShellDetailsFactory; - this.deploymentCommandsFactory = deploymentCommandsFactory; - } - - public DeployedInstanceManager createFRC(InetAddress address) { - return createFRC(address, NullOutputStream::new, NullOutputStream::new); - } - - public DeployedInstanceManager createFRC(InetAddress address, Supplier stdOut, Supplier stdErr) { - final File projectFile = project.getFile().get(); - return createFRC(address, projectFile, stdOut, stdErr); - } - - public DeployedInstanceManager createFRC(InetAddress address, File projectFile) { - return createFRC(address, projectFile, NullOutputStream::new, NullOutputStream::new); - } - - public DeployedInstanceManager createFRC(InetAddress addresses, File projectFile, Supplier stdOut, Supplier stdErr) { - return new DeployedInstanceManager(eventBus, coreJAR, projectFile, secureShellDetailsFactory.createFRC(addresses), deploymentCommandsFactory.createFRC(), stdOut, stdErr); - } - } - - /** - * @param eventBus - * @param coreJar The jar with all of the core. This will be copied to the destination - * @param projectFile The project file to send over - * @param details The details regarding connecting to the secure shell - * @param deploymentCommands The commands required to start and stop GRIP - * @param stdOut Supplies the stream to be used for the standard output from the ssh command - * @param stdErr Supplies the stream to be used for the standard error from the ssh command - */ - private DeployedInstanceManager(EventBus eventBus, - File coreJar, - File projectFile, - SecureShellDetails details, - DeploymentCommands deploymentCommands, - Supplier stdOut, - Supplier stdErr) { - this.eventBus = checkNotNull(eventBus, "The event bus can not be null"); - this.coreJar = checkNotNull(coreJar, "The URI of the coreJar can not be null"); - this.projectFile = checkNotNull(projectFile, "The project file can not be null"); - this.details = checkNotNull(details, "The details can not be null"); - this.deploymentCommands = checkNotNull(deploymentCommands, "The deployment commands can not be null"); - this.stdOut = checkNotNull(stdOut, "The standard out stream supplier can not be null"); - this.stdErr = checkNotNull(stdErr, "The standard err stream supplier can not be null"); - sshThread = Optional.empty(); - } - - /** - * Deploys the coreJar and the project file to the remote device - * - */ - public synchronized Promise deploy() { - final DeployedInstanceManager self = this; - JSch.setConfig("StrictHostKeyChecking", "no"); - final DeferredManager deferred = new DefaultDeferredManager(); - - return deferred.when(new DeferredCallable() { - @Override - public DeployedInstanceManager call() throws IOException { - notify(0.2); - scpFileToTarget(coreJar); - notify(0.5); - scpFileToTarget(projectFile); - notify(1.0); - return self; - } - - }); - } - - /** - * @param file The file to send - * @throws IOException If there is a problem sending the file. - */ - private void scpFileToTarget(File file) throws IOException { - final String localFile = URLDecoder.decode(Paths.get(file.toURI()).toString()); - try { - final Scp scp = details.createSCPRunner(); - scp.setLocalFile(localFile); - scp.execute(); - } catch (BuildException e) { - throw new IOException("Failed to deploy", e); - } - } - - /** - * Starts GRIP running on the device specified by the secure shell details - * - * @throws IOException - */ - public synchronized void start() throws IOException { - if (isStarted()) { - throw new IllegalStateException("The program has already been started and must be stopped before restarting"); - } - // Ensure that the project isn't running from a previous instance. - runStop(); - Thread launcher = new Thread(() -> { - try { - final Shell gripShell = new Shell.Safe(details.createSSHShell()); - gripShell.exec("nohup " + deploymentCommands.getJARLaunchCommand(coreJar.getName(), projectFile.getName()) + " &", - new NullInputStream(0L), - stdOut.get(), - stdErr.get()); - } catch (IOException e) { - throw new IllegalStateException("The program failed to start", e); - } finally { - // This thread is done, shut it down. - synchronized (this) { - sshThread = Optional.empty(); - } - } - }, "SSH Monitor Thread"); - launcher.setUncaughtExceptionHandler((thread, exception) -> { - eventBus.post(new UnexpectedThrowableEvent(exception, "Failed to start the remote instance of the application")); - try { - runStop(); - } catch (IOException e) { - eventBus.post(new UnexpectedThrowableEvent(e, "Failed to stop the remote instance of the program")); - } - }); - launcher.setDaemon(true); - launcher.start(); - this.sshThread = Optional.of(launcher); - eventBus.post(new StartedStoppedEvent(this)); - } - - /** - * Stops the program running on the remote device - * - * @throws IOException If the command fails to be delivered - */ - public synchronized void stop() throws IOException { - if (!isStarted()) { - throw new IllegalStateException("The program hasn't started yet."); - } - runStop(); - do { - try { - // Since we hold the mutex on this we can wait - wait(50); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - logger.log(Level.WARNING, "Wait interrupted", e); - } - runStop(); - } while (isStarted() && !Thread.interrupted()); - eventBus.post(new StartedStoppedEvent(this)); - } - - /** - * Makes an SSH connection to the remote and runs the kill command. - * Should block until the command has executed. - * @throws IOException If the shell fails. - */ - private void runStop() throws IOException { - final Shell.Plain gripShell = new Shell.Plain(new Shell.Safe(details.createSSHShell())); - gripShell.exec(deploymentCommands.getKillCommand(coreJar.getName())); - } - - @Override - public synchronized boolean isStarted() { - return sshThread.isPresent() && sshThread.get().isAlive(); - } - -} diff --git a/ui/src/main/java/edu/wpi/grip/ui/util/deployment/DeploymentCommands.java b/ui/src/main/java/edu/wpi/grip/ui/util/deployment/DeploymentCommands.java deleted file mode 100644 index e1048b13d1..0000000000 --- a/ui/src/main/java/edu/wpi/grip/ui/util/deployment/DeploymentCommands.java +++ /dev/null @@ -1,54 +0,0 @@ -package edu.wpi.grip.ui.util.deployment; - - -import com.google.inject.Singleton; - -import java.util.function.Function; - -import static com.google.common.base.Preconditions.checkNotNull; - - -/** - * The commands to be used to launch and kill the deployed program. - */ -public class DeploymentCommands { - protected static final String DEFAULT_JAVA_COMMAND = "java"; - protected static final Function - DEFAULT_KILL_BY_NAME = name -> - "kill $(ps aux | grep \"" + name + "\" | grep -v 'grep' | awk '{print $1}') || :"; - private final String javaCommand; - private final Function killByNameCommand; - - @Singleton - public static class Factory { - public DeploymentCommands createFRC() { - return new DeploymentCommands("/usr/local/frc/JRE/bin/java", DEFAULT_KILL_BY_NAME); - } - } - - DeploymentCommands(String javaCommand, Function killByNameCommand) { - this.javaCommand = checkNotNull(javaCommand, "The java command can not be null"); - this.killByNameCommand = checkNotNull(killByNameCommand, "The kill by name consumer can not be null"); - } - - public DeploymentCommands() { - this(DEFAULT_JAVA_COMMAND, DEFAULT_KILL_BY_NAME); - } - - /** - * @param jarFile The name of the jar file - * @param projectFile The name of the project file - * @return The launch command - */ - protected String getJARLaunchCommand(String jarFile, String projectFile) { - return this.javaCommand + " -jar " + jarFile + " " + projectFile; - } - - /** - * @param name The name of the process to kill - * @return The command to kill the program running remotely - */ - protected String getKillCommand(String name) { - return killByNameCommand.apply(name); - } -} diff --git a/ui/src/main/java/edu/wpi/grip/ui/util/deployment/SecureShellDetails.java b/ui/src/main/java/edu/wpi/grip/ui/util/deployment/SecureShellDetails.java deleted file mode 100644 index e2c21019ce..0000000000 --- a/ui/src/main/java/edu/wpi/grip/ui/util/deployment/SecureShellDetails.java +++ /dev/null @@ -1,107 +0,0 @@ -package edu.wpi.grip.ui.util.deployment; - -import com.google.inject.Singleton; -import com.jcabi.ssh.SSHByPassword; -import com.jcabi.ssh.Shell; -import org.apache.tools.ant.Project; -import org.apache.tools.ant.taskdefs.optional.ssh.Scp; - -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.Optional; - -import static com.google.common.base.Preconditions.checkNotNull; - -/** - * Contains all of the details for securely copying grip core and a save file to another device. - */ -public class SecureShellDetails { - private static final int DEFAULT_PORT = 22; - - private final String userSSH; - private final Optional password; - private final String host; - private final Optional remoteDir; - private final int port; - - @Singleton - public static class Factory { - SecureShellDetails createFRC(InetAddress address) { - return new SecureShellDetails("lvuser", address.getHostAddress()); - } - } - - /** - * @param userSSH The username to connect to - * @param password The password to use, nullable - * @param host The host's address - * @param remoteDir The remote directory to ssh into - * @param port The port to to use. - */ - public SecureShellDetails(String userSSH, String password, String host, String remoteDir, int port) { - this.userSSH = checkNotNull(userSSH, "userSSH can not be null"); - this.password = Optional.ofNullable(password); - this.host = checkNotNull(host, "host can not be null"); - this.remoteDir = Optional.ofNullable(remoteDir); - this.port = port; - } - - public SecureShellDetails(String userSSH, String password, String serverSSH, String remoteDir) { - this(userSSH, password, serverSSH, remoteDir, DEFAULT_PORT); - } - - public SecureShellDetails(String userSSH, String host, String remoteDir) { - this(userSSH, null, host, remoteDir, DEFAULT_PORT); - } - - public SecureShellDetails(String userSSH, String host) { - this(userSSH, null, host, null, DEFAULT_PORT); - } - - - protected int getPort() { - return port; - } - - protected String host() { - return host; - } - - public String getUserSSH() { - return userSSH; - } - - protected Optional getPassword() { - return password; - } - - protected Scp createSCPRunner() { - Scp scp = new Scp(); - scp.setPort(getPort()); - scp.setPassword(getPassword().orElse("")); - scp.setTodir(getToDir()); - scp.setProject(new Project()); - scp.setTrust(true); - return scp; - } - - protected Shell createSSHShell() throws UnknownHostException { - return new SSHByPassword(host(), getPort(), getUserSSH(), getPassword().orElse("")); - } - - /** - * @return The directory to SCP the files to - */ - protected String getToDir() { - // userSSH + ":" + password + "@" + srvrSSH + ":" + remoteDir; - String sshDirCommand = userSSH; - if (password.isPresent()) { - sshDirCommand += (":" + password.get()); - } - sshDirCommand += ("@" + host + ":"); - if (remoteDir.isPresent()) { - sshDirCommand += remoteDir.get(); - } - return sshDirCommand; - } -} diff --git a/ui/src/main/resources/edu/wpi/grip/ui/DeployerPane.fxml b/ui/src/main/resources/edu/wpi/grip/ui/DeployerPane.fxml deleted file mode 100644 index ba6197545d..0000000000 --- a/ui/src/main/resources/edu/wpi/grip/ui/DeployerPane.fxml +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - - - - - -
- - - - -