/******************************************************************************* * 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.ide.api.machine.execagent; import com.google.inject.Inject; import org.eclipse.che.api.core.jsonrpc.commons.RequestTransmitter; import org.eclipse.che.api.core.jsonrpc.commons.JsonRpcPromise; import org.eclipse.che.api.core.model.machine.Command; import org.eclipse.che.api.machine.shared.dto.execagent.GetProcessLogsRequestDto; import org.eclipse.che.api.machine.shared.dto.execagent.GetProcessLogsResponseDto; import org.eclipse.che.api.machine.shared.dto.execagent.GetProcessRequestDto; import org.eclipse.che.api.machine.shared.dto.execagent.GetProcessResponseDto; import org.eclipse.che.api.machine.shared.dto.execagent.GetProcessesRequestDto; import org.eclipse.che.api.machine.shared.dto.execagent.GetProcessesResponseDto; import org.eclipse.che.api.machine.shared.dto.execagent.ProcessKillRequestDto; import org.eclipse.che.api.machine.shared.dto.execagent.ProcessKillResponseDto; import org.eclipse.che.api.machine.shared.dto.execagent.ProcessStartRequestDto; import org.eclipse.che.api.machine.shared.dto.execagent.ProcessStartResponseDto; import org.eclipse.che.api.machine.shared.dto.execagent.ProcessSubscribeRequestDto; import org.eclipse.che.api.machine.shared.dto.execagent.ProcessSubscribeResponseDto; import org.eclipse.che.api.machine.shared.dto.execagent.ProcessUnSubscribeRequestDto; import org.eclipse.che.api.machine.shared.dto.execagent.ProcessUnSubscribeResponseDto; import org.eclipse.che.api.machine.shared.dto.execagent.UpdateSubscriptionRequestDto; import org.eclipse.che.api.machine.shared.dto.execagent.UpdateSubscriptionResponseDto; import org.eclipse.che.api.machine.shared.dto.execagent.event.DtoWithPid; import org.eclipse.che.api.machine.shared.dto.execagent.event.ProcessDiedEventDto; import org.eclipse.che.api.machine.shared.dto.execagent.event.ProcessStartedEventDto; import org.eclipse.che.api.machine.shared.dto.execagent.event.ProcessStdErrEventDto; import org.eclipse.che.api.machine.shared.dto.execagent.event.ProcessStdOutEventDto; import org.eclipse.che.ide.api.machine.ExecAgentCommandManager; import org.eclipse.che.ide.api.machine.ExecAgentEventManager; import org.eclipse.che.ide.dto.DtoFactory; import org.eclipse.che.ide.util.loging.Log; import javax.inject.Singleton; import java.util.List; import java.util.function.Consumer; import static org.eclipse.che.ide.util.StringUtils.join; /** * Implementation of exec-agent command manager based on JSON RPC protocol that * uses bi-directional request/response transporting. */ @Singleton public class JsonRpcExecAgentCommandManager implements ExecAgentCommandManager { public static final String PROCESS_START = "process.start"; public static final String PROCESS_KILL = "process.kill"; public static final String PROCESS_SUBSCRIBE = "process.subscribe"; public static final String PROCESS_UNSUBSCRIBE = "process.unsubscribe"; public static final String PROCESS_UPDATE_SUBSCRIBER = "process.updateSubscriber"; public static final String PROCESS_GET_LOGS = "process.getLogs"; public static final String PROCESS_GET_PROCESS = "process.getProcess"; public static final String PROCESS_GET_PROCESSES = "process.getProcesses"; private final DtoFactory dtoFactory; private final RequestTransmitter transmitter; private final ExecAgentEventManager eventManager; @Inject protected JsonRpcExecAgentCommandManager(DtoFactory dtoFactory, RequestTransmitter transmitter, ExecAgentEventManager eventManager) { this.dtoFactory = dtoFactory; this.transmitter = transmitter; this.eventManager = eventManager; } @Override public ExecAgentConsumer<ProcessStartResponseDto> startProcess(final String endpointId, Command command) { String name = command.getName(); String commandLine = command.getCommandLine(); String type = command.getType(); Log.debug(getClass(), "Starting a process. Name: " + name + ", command line: " + commandLine + ", type: " + type); ProcessStartRequestDto dto = dtoFactory.createDto(ProcessStartRequestDto.class) .withCommandLine(commandLine) .withName(name) .withType(type); final ExecAgentConsumer<ProcessStartResponseDto> execAgentConsumer = new ExecAgentConsumer<>(); transmitter.newRequest() .endpointId(endpointId) .methodName(PROCESS_START) .paramsAsDto(dto) .sendAndReceiveResultAsDto(ProcessStartResponseDto.class) .onSuccess(processStartResponseDto -> subscribe(endpointId, execAgentConsumer, processStartResponseDto)); return execAgentConsumer; } @Override public JsonRpcPromise<ProcessKillResponseDto> killProcess(String endpointId, final int pid) { Log.debug(getClass(), "Killing a process. PID: " + pid); ProcessKillRequestDto dto = dtoFactory.createDto(ProcessKillRequestDto.class).withPid(pid); return transmitter.newRequest() .endpointId(endpointId) .methodName(PROCESS_KILL) .paramsAsDto(dto) .sendAndReceiveResultAsDto(ProcessKillResponseDto.class); } @Override public ExecAgentConsumer<ProcessSubscribeResponseDto> subscribe(final String endpointId, int pid, List<String> eventTypes, String after) { Log.debug(getClass(), "Subscribing to a process. PID: " + pid + ", event types: " + eventTypes + ", after timestamp: " + after); ProcessSubscribeRequestDto dto = dtoFactory.createDto(ProcessSubscribeRequestDto.class) .withPid(pid) .withEventTypes(join(eventTypes, ",")) .withAfter(after); final ExecAgentConsumer<ProcessSubscribeResponseDto> execAgentConsumer = new ExecAgentConsumer<>(); transmitter.newRequest() .endpointId(endpointId) .methodName(PROCESS_SUBSCRIBE) .paramsAsDto(dto) .sendAndReceiveResultAsDto(ProcessSubscribeResponseDto.class) .onSuccess((s, processSubscribeResponseDto) -> subscribe(endpointId, execAgentConsumer, processSubscribeResponseDto)); return execAgentConsumer; } @Override public JsonRpcPromise<ProcessUnSubscribeResponseDto> unsubscribe(String endpointId, int pid, List<String> eventTypes, String after) { Log.debug(getClass(), "Unsubscribing to a process. PID: " + pid + ", event types: " + eventTypes + ", after timestamp: " + after); final ProcessUnSubscribeRequestDto dto = dtoFactory.createDto(ProcessUnSubscribeRequestDto.class) .withPid(pid) .withEventTypes(join(eventTypes, ",")) .withAfter(after); return transmitter.newRequest() .endpointId(endpointId) .methodName(PROCESS_UNSUBSCRIBE) .paramsAsDto(dto) .sendAndReceiveResultAsDto(ProcessUnSubscribeResponseDto.class); } @Override public JsonRpcPromise<UpdateSubscriptionResponseDto> updateSubscription(String endpointId, int pid, List<String> eventTypes) { Log.debug(getClass(), "Updating subscription to a process. PID: " + pid + ", event types: " + eventTypes); final UpdateSubscriptionRequestDto dto = dtoFactory.createDto(UpdateSubscriptionRequestDto.class) .withPid(pid) .withEventTypes(join(eventTypes, ",")); return transmitter.newRequest() .endpointId(endpointId) .methodName(PROCESS_UPDATE_SUBSCRIBER) .paramsAsDto(dto) .sendAndReceiveResultAsDto(UpdateSubscriptionResponseDto.class); } @Override public JsonRpcPromise<List<GetProcessLogsResponseDto>> getProcessLogs(String endpointId, int pid, String from, String till, int limit, int skip) { Log.debug(getClass(), "Getting process logs" + ". PID: " + pid + ", from: " + from + ", till: " + till + ", limit: " + limit + ", skip: " + skip); GetProcessLogsRequestDto dto = dtoFactory.createDto(GetProcessLogsRequestDto.class) .withPid(pid) .withFrom(from) .withTill(till) .withLimit(limit) .withSkip(skip); return transmitter.newRequest() .endpointId(endpointId) .methodName(PROCESS_GET_LOGS) .paramsAsDto(dto) .sendAndReceiveResultAsListOfDto(GetProcessLogsResponseDto.class); } @Override public JsonRpcPromise<GetProcessResponseDto> getProcess(String endpointId, int pid) { Log.debug(getClass(), "Getting process info. PID: " + pid); GetProcessRequestDto dto = dtoFactory.createDto(GetProcessRequestDto.class).withPid(pid); return transmitter.newRequest() .endpointId(endpointId) .methodName(PROCESS_GET_PROCESS) .paramsAsDto(dto) .sendAndReceiveResultAsDto(GetProcessResponseDto.class); } @Override public JsonRpcPromise<List<GetProcessesResponseDto>> getProcesses(String endpointId, boolean all) { Log.debug(getClass(), "Getting processes info. All: " + all); GetProcessesRequestDto dto = dtoFactory.createDto(GetProcessesRequestDto.class).withAll(all); return transmitter.newRequest() .endpointId(endpointId) .methodName(PROCESS_GET_PROCESSES) .paramsAsDto(dto) .sendAndReceiveResultAsListOfDto(GetProcessesResponseDto.class); } private <T extends DtoWithPid> void subscribe(String endpointId, ExecAgentConsumer<T> promise, T arg) { final int pid = arg.getPid(); if (promise.hasProcessDiedEventConsumer()) { Consumer<ProcessDiedEventDto> consumer = promise.getProcessDiedEventDtoConsumer(); eventManager.registerProcessDiedConsumer(endpointId, pid, consumer); } if (promise.hasProcessStartedEventConsumer()) { Consumer<ProcessStartedEventDto> consumer = promise.getProcessStartedEventDtoConsumer(); eventManager.registerProcessStartedConsumer(endpointId, pid, consumer); } if (promise.hasProcessStdOutEventConsumer()) { Consumer<ProcessStdOutEventDto> consumer = promise.getProcessStdOutEventDtoConsumer(); eventManager.registerProcessStdOutConsumer(endpointId, pid, consumer); } if (promise.hasProcessStdErrEventConsumer()) { Consumer<ProcessStdErrEventDto> consumer = promise.getProcessStdErrEventDtoConsumer(); eventManager.registerProcessStdErrConsumer(endpointId, pid, consumer); } if (promise.hasOperation()) { promise.getConsumer().accept(arg); } } }