package com.atsebak.embeddedlinuxjvm.protocol.ssh;
import com.atsebak.embeddedlinuxjvm.commandline.JavaStatusChecker;
import com.atsebak.embeddedlinuxjvm.commandline.LinuxCommand;
import com.atsebak.embeddedlinuxjvm.console.EmbeddedLinuxJVMConsoleView;
import com.atsebak.embeddedlinuxjvm.localization.EmbeddedLinuxJVMBundle;
import com.atsebak.embeddedlinuxjvm.protocol.ssh.jsch.EmbeddedSSHClient;
import com.atsebak.embeddedlinuxjvm.protocol.ssh.jsch.SFTPHandler;
import com.atsebak.embeddedlinuxjvm.runner.data.EmbeddedLinuxJVMRunConfigurationRunnerParameters;
import com.atsebak.embeddedlinuxjvm.utils.FileUtilities;
import com.intellij.execution.configurations.RuntimeConfigurationException;
import com.intellij.execution.ui.ConsoleViewContentType;
import com.intellij.notification.Notification;
import com.intellij.notification.NotificationType;
import com.intellij.notification.Notifications;
import com.intellij.openapi.application.ApplicationManager;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import lombok.Builder;
import lombok.SneakyThrows;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Vector;
@Builder
public class SSHHandlerTarget {
public static final String NEW_LINE = System.getProperty("line.separator");
private static final String OUTPUT_LOCATION = "IdeaProjects";
private static final String EMBEDDED_LINUX_HOME = "home";
private EmbeddedLinuxJVMRunConfigurationRunnerParameters params;
private EmbeddedLinuxJVMConsoleView consoleView;
private EmbeddedSSHClient ssh;
/**
* Uploads Java application output folders
*
* @param compileOutput Output directory folder where to store the java application
* @param cmd The command to execute on the java files
* @throws IOException
* @throws ClassNotFoundException
* @throws RuntimeConfigurationException
*/
public void uploadAndRunJavaApp(@NotNull final File compileOutput, @NotNull final String cmd)
throws IOException, ClassNotFoundException, RuntimeConfigurationException {
final String remoteDir = FileUtilities.SEPARATOR + EMBEDDED_LINUX_HOME + FileUtilities.SEPARATOR
+ params.getUsername() + FileUtilities.SEPARATOR + OUTPUT_LOCATION;
String deploymentPath = remoteDir + FileUtilities.SEPARATOR + consoleView.getProject().getName();
genericUpload(deploymentPath, compileOutput);
runJavaApp(deploymentPath, cmd);
}
/**
* Generic SSh Ftp uploader
*
* @param deploymentPath the remote location storing the compressed file
* @param fileToUpload files to upload
* @throws IOException
* @throws RuntimeConfigurationException
*/
public void genericUpload(@NotNull final String deploymentPath, @NotNull final File fileToUpload) throws IOException, RuntimeConfigurationException {
forceCreateDirectories(deploymentPath);
Session session = connect(ssh.get());
try {
SFTPHandler sftpHandler = new SFTPHandler(consoleView);
sftpHandler.upload(session, fileToUpload, deploymentPath);
} catch (Exception e) {
setErrorOnUI(e.getMessage());
}
}
/**
* Force create directories, if it exists it won't do anything
*
* @param path
* @throws IOException
* @throws RuntimeConfigurationException
*/
private void forceCreateDirectories(@NotNull final String path) throws IOException, RuntimeConfigurationException {
Session session = connect(ssh.get());
try {
ChannelExec channelExec = (ChannelExec) session.openChannel("exec");
List<String> commands = Arrays.asList(
String.format("mkdir -p %s", path),
String.format("cd %s", path),
String.format("mkdir -p %s", FileUtilities.CLASSES),
String.format("mkdir -p %s", FileUtilities.LIB),
String.format("cd %s", path + FileUtilities.SEPARATOR + FileUtilities.CLASSES),
"rm -rf *"
);
for (String command : commands) {
consoleView.print(EmbeddedLinuxJVMBundle.getString("pi.deployment.command") + command + NEW_LINE, ConsoleViewContentType.SYSTEM_OUTPUT);
}
channelExec.setCommand(LinuxCommand.builder().commands(commands).build().toString());
channelExec.connect();
channelExec.disconnect();
} catch (JSchException e) {
setErrorOnUI(e.getMessage());
}
}
/**
* Runs that java app with the specified command and then takes the console output from target to host machine
*
* @param path
* @param cmd
* @throws IOException
*/
private void runJavaApp(@NotNull final String path, @NotNull final String cmd) throws IOException, RuntimeConfigurationException {
consoleView.print(NEW_LINE + EmbeddedLinuxJVMBundle.getString("pi.deployment.build") + NEW_LINE + NEW_LINE, ConsoleViewContentType.SYSTEM_OUTPUT);
Session session = connect(ssh.get());
consoleView.setSession(session);
try {
ChannelExec channelExec = (ChannelExec) session.openChannel("exec");
channelExec.setOutputStream(System.out, true);
channelExec.setErrStream(System.err, true);
List<String> commands = Arrays.asList(
String.format("%s kill -9 $(ps -efww | grep \"%s\"| grep -v grep | tr -s \" \"| cut -d\" \" -f2)", params.isRunAsRoot() ? "sudo" : "", params.getMainclass()),
String.format("cd %s", path),
String.format("tar -xvf %s.tar", consoleView.getProject().getName()),
"rm *.tar",
cmd);
for (String command : commands) {
consoleView.print(EmbeddedLinuxJVMBundle.getString("pi.deployment.command") + command + NEW_LINE, ConsoleViewContentType.SYSTEM_OUTPUT);
}
channelExec.setCommand(LinuxCommand.builder().commands(commands).build().toString());
channelExec.connect();
checkOnProcess(channelExec);
} catch (JSchException e) {
setErrorOnUI(e.getMessage());
}
}
/**
* Checks if command is done running
*/
private void checkOnProcess(@NotNull final ChannelExec channelExec) {
ApplicationManager.getApplication().executeOnPooledThread(new JavaStatusChecker(channelExec, consoleView));
}
public Vector getAlreadyDeployedLibraries() throws JSchException {
final String remoteDir = FileUtilities.SEPARATOR + EMBEDDED_LINUX_HOME + FileUtilities.SEPARATOR
+ params.getUsername() + FileUtilities.SEPARATOR + OUTPUT_LOCATION;
String path = remoteDir + FileUtilities.SEPARATOR + consoleView.getProject().getName();
String jarsPath = path + FileUtilities.SEPARATOR + FileUtilities.LIB;
SFTPHandler handler = new SFTPHandler();
Session session = connect(ssh.get());
return handler.getFiles(session, jarsPath);
}
/**
* Authenticates and connects to remote target via ssh protocol
* @param session
* @return
*/
@SneakyThrows({RuntimeConfigurationException.class})
private Session connect(Session session) {
if (!session.isConnected()) {
session = EmbeddedSSHClient.builder()
.username(params.getUsername())
.password(params.getPassword())
.hostname(params.getHostname())
.port(params.getSshPort())
.build().get();
if (!session.isConnected()) {
setErrorOnUI(EmbeddedLinuxJVMBundle.getString("ssh.remote.error"));
throw new RuntimeConfigurationException(EmbeddedLinuxJVMBundle.getString("ssh.remote.error"));
} else {
return session;
}
}
return session;
}
/**
* Sets errors on the UI
*
* @param message
*/
private void setErrorOnUI(@NotNull final String message) {
final Notification notification = new Notification(
com.atsebak.embeddedlinuxjvm.utils.Notifications.GROUPDISPLAY_ID,
EmbeddedLinuxJVMBundle.getString("pi.ssh.connection.error"), message,
NotificationType.INFORMATION);
Notifications.Bus.notify(notification);
}
}