/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.plugin.machine.ssh;
import org.eclipse.che.api.core.NotFoundException;
import org.eclipse.che.api.core.model.machine.Command;
import org.eclipse.che.api.core.model.machine.Machine;
import org.eclipse.che.api.core.model.machine.MachineConfig;
import org.eclipse.che.api.core.model.machine.MachineStatus;
import org.eclipse.che.api.core.model.machine.ServerConf;
import org.eclipse.che.api.core.util.LineConsumer;
import org.eclipse.che.api.machine.server.exception.MachineException;
import org.eclipse.che.api.machine.server.model.impl.MachineRuntimeInfoImpl;
import org.eclipse.che.api.machine.server.model.impl.ServerImpl;
import org.eclipse.che.api.machine.server.spi.InstanceNode;
import javax.ws.rs.core.UriBuilder;
import java.io.IOException;
import java.net.URI;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import static java.lang.String.format;
import static java.util.Collections.emptyMap;
/**
* Implementation of machine that represents ssh machine.
*
* @author Alexander Garagatyi
* @author Max Shaposhnik
* @see SshMachineInstanceProvider
*/
// todo try to avoid map of processes
public class SshMachineInstance {
private static final AtomicInteger pidSequence = new AtomicInteger(1);
private String id;
private String workspaceId;
private final String envName;
private final String owner;
private MachineRuntimeInfoImpl machineRuntime;
private final MachineConfig machineConfig;
private final SshClient sshClient;
private final LineConsumer outputConsumer;
private final SshMachineFactory machineFactory;
private MachineStatus status;
private final Set<ServerConf> machinesServers;
private final ConcurrentHashMap<Integer, SshMachineProcess> machineProcesses;
public SshMachineInstance(Machine machine,
SshClient sshClient,
LineConsumer outputConsumer,
SshMachineFactory machineFactory,
Set<ServerConf> machinesServers) {
this.id = machine.getId();
this.workspaceId = machine.getWorkspaceId();
this.envName = machine.getEnvName();
this.owner = machine.getOwner();
this.sshClient = sshClient;
this.outputConsumer = outputConsumer;
this.machineFactory = machineFactory;
this.machineConfig = machine.getConfig();
this.status = machine.getStatus();
this.machinesServers = new HashSet<>(machinesServers.size() + machine.getConfig().getServers().size());
this.machinesServers.addAll(machinesServers);
this.machinesServers.addAll(machine.getConfig().getServers());
this.machineProcesses = new ConcurrentHashMap<>();
}
public LineConsumer getLogger() {
return outputConsumer;
}
public MachineRuntimeInfoImpl getRuntime() {
// lazy initialization
if (machineRuntime == null) {
synchronized (this) {
if (machineRuntime == null) {
UriBuilder uriBuilder = UriBuilder.fromUri("http://" + sshClient.getHost());
final Map<String, ServerImpl> servers = new HashMap<>();
for (ServerConf serverConf : machinesServers) {
servers.put(serverConf.getPort(), serverConfToServer(serverConf, uriBuilder.clone()));
}
machineRuntime = new MachineRuntimeInfoImpl(emptyMap(), emptyMap(), servers);
}
}
// todo get env from client
}
return machineRuntime;
}
public SshMachineProcess getProcess(final int pid) throws NotFoundException, MachineException {
final SshMachineProcess machineProcess = machineProcesses.get(pid);
if (machineProcess == null) {
throw new NotFoundException(format("Process with pid %s not found", pid));
}
try {
machineProcess.checkAlive();
return machineProcess;
} catch (NotFoundException e) {
machineProcesses.remove(pid);
throw e;
}
}
public List<SshMachineProcess> getProcesses() throws MachineException {
// todo get children of session process
return machineProcesses.values()
.stream()
.filter(SshMachineProcess::isAlive)
.collect(Collectors.toList());
}
public SshMachineProcess createProcess(Command command, String outputChannel) throws MachineException {
final Integer pid = pidSequence.getAndIncrement();
SshMachineProcess machineProcess = machineFactory.createInstanceProcess(command, outputChannel, pid, sshClient);
machineProcesses.put(pid, machineProcess);
return machineProcess;
}
public void destroy() throws MachineException {
try {
outputConsumer.close();
} catch (IOException ignored) {
}
// session destroying stops all processes
// todo kill all processes started by code, we should get parent pid of session and kill all children
sshClient.stop();
}
public InstanceNode getNode() {
return null;// todo
}
public MachineStatus getStatus() {
return status;
}
public void setStatus(MachineStatus status) {
this.status = status;
}
public String getId() {
return id;
}
public void copy(String sourcePath, String targetPath) throws MachineException {
sshClient.copy(sourcePath, targetPath);
}
private ServerImpl serverConfToServer(ServerConf serverConf, UriBuilder uriBuilder) {
String port = serverConf.getPort().split("/")[0];
uriBuilder.port(Integer.parseInt(port));
if (serverConf.getPath() != null) {
uriBuilder.path(serverConf.getPath());
}
URI serverUri = uriBuilder.build();
return new ServerImpl(serverConf.getRef(),
serverConf.getProtocol(),
serverUri.getHost() + ":" + serverUri.getPort(),
serverConf.getProtocol() != null ? serverUri.toString() : null,
null);
}
public String getWorkspaceId() {
return workspaceId;
}
public MachineConfig getMachineConfig() {
return machineConfig;
}
public String getEnvName() {
return envName;
}
public String getOwner() {
return owner;
}
}