/******************************************************************************* * 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.console; import com.google.gwt.user.client.ui.AcceptsOneWidget; import com.google.inject.Inject; import com.google.inject.assistedinject.Assisted; import com.google.web.bindery.event.shared.EventBus; import org.eclipse.che.api.core.model.machine.Machine; import org.eclipse.che.api.machine.shared.dto.MachineProcessDto; import org.eclipse.che.api.machine.shared.dto.execagent.ProcessSubscribeResponseDto; 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.api.promises.client.Operation; import org.eclipse.che.api.promises.client.OperationException; import org.eclipse.che.commons.annotation.Nullable; import org.eclipse.che.ide.api.command.CommandExecutor; import org.eclipse.che.ide.api.command.CommandImpl; import org.eclipse.che.ide.api.machine.ExecAgentCommandManager; import org.eclipse.che.ide.api.machine.events.ProcessFinishedEvent; import org.eclipse.che.ide.api.machine.events.ProcessStartedEvent; import org.eclipse.che.ide.api.macro.MacroProcessor; import org.eclipse.che.ide.machine.MachineResources; import org.vectomatic.dom.svg.ui.SVGResource; import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; import static com.google.common.base.Strings.isNullOrEmpty; import static org.eclipse.che.api.workspace.shared.Constants.COMMAND_PREVIEW_URL_ATTRIBUTE_NAME; /** * Console for command output. * * @author Artem Zatsarynnyi */ public class CommandOutputConsolePresenter implements CommandOutputConsole, OutputConsoleView.ActionDelegate { private final OutputConsoleView view; private final MachineResources resources; private final CommandImpl command; private final EventBus eventBus; private final Machine machine; private final CommandExecutor commandExecutor; private final ExecAgentCommandManager execAgentCommandManager; private int pid; private boolean finished; /** Wrap text or not */ private boolean wrapText = false; /** Follow output when printing text */ private boolean followOutput = true; private final List<ActionDelegate> actionDelegates = new ArrayList<>(); @Inject public CommandOutputConsolePresenter(final OutputConsoleView view, MachineResources resources, CommandExecutor commandExecutor, MacroProcessor macroProcessor, EventBus eventBus, ExecAgentCommandManager execAgentCommandManager, @Assisted CommandImpl command, @Assisted Machine machine) { this.view = view; this.resources = resources; this.execAgentCommandManager = execAgentCommandManager; this.command = command; this.machine = machine; this.eventBus = eventBus; this.commandExecutor = commandExecutor; view.setDelegate(this); final String previewUrl = command.getAttributes().get(COMMAND_PREVIEW_URL_ATTRIBUTE_NAME); if (!isNullOrEmpty(previewUrl)) { macroProcessor.expandMacros(previewUrl).then(new Operation<String>() { @Override public void apply(String arg) throws OperationException { view.showPreviewUrl(arg); } }); } else { view.hidePreview(); } view.showCommandLine(command.getCommandLine()); } @Override public void go(AcceptsOneWidget container) { container.setWidget(view); } @Override public CommandImpl getCommand() { return command; } @Nullable @Override public int getPid() { return pid; } @Override public String getTitle() { return command.getName(); } @Override public SVGResource getTitleIcon() { return resources.output(); } @Override public void listenToOutput(String wsChannel) { } @Override public void attachToProcess(MachineProcessDto process) { } @Override public Consumer<ProcessStdErrEventDto> getStdErrConsumer() { return event -> { String text = event.getText(); boolean carriageReturn = text.endsWith("\r"); String color = "red"; view.print(text, carriageReturn, color); for (ActionDelegate actionDelegate : actionDelegates) { actionDelegate.onConsoleOutput(CommandOutputConsolePresenter.this); } }; } @Override public Consumer<ProcessStdOutEventDto> getStdOutConsumer() { return event -> { String stdOutMessage = event.getText(); boolean carriageReturn = stdOutMessage.endsWith("\r"); view.print(stdOutMessage, carriageReturn); for (ActionDelegate actionDelegate : actionDelegates) { actionDelegate.onConsoleOutput(CommandOutputConsolePresenter.this); } }; } @Override public Consumer<ProcessStartedEventDto> getProcessStartedConsumer() { return event -> { finished = false; view.enableStopButton(true); view.toggleScrollToEndButton(true); pid = event.getPid(); eventBus.fireEvent(new ProcessStartedEvent(pid, machine)); }; } @Override public Consumer<ProcessDiedEventDto> getProcessDiedConsumer() { return event -> { finished = true; view.enableStopButton(false); view.toggleScrollToEndButton(false); eventBus.fireEvent(new ProcessFinishedEvent(pid, machine)); }; } @Override public Consumer<ProcessSubscribeResponseDto> getProcessSubscribeConsumer() { return process -> pid = process.getPid(); } @Override public void printOutput(String output) { view.print(output.replaceAll("\\[STDOUT\\] ", ""), false); } @Override public boolean isFinished() { return finished; } @Override public void stop() { execAgentCommandManager.killProcess(machine.getId(), pid); } @Override public void close() { actionDelegates.clear(); } @Override public void addActionDelegate(ActionDelegate actionDelegate) { actionDelegates.add(actionDelegate); } @Override public void reRunProcessButtonClicked() { if (isFinished()) { commandExecutor.executeCommand(command, machine); } else { execAgentCommandManager.killProcess(machine.getId(), pid) .onSuccess(() -> commandExecutor.executeCommand(command, machine)); } } @Override public void stopProcessButtonClicked() { stop(); } @Override public void clearOutputsButtonClicked() { view.clearConsole(); } @Override public void downloadOutputsButtonClicked() { for (ActionDelegate actionDelegate : actionDelegates) { actionDelegate.onDownloadOutput(this); } } @Override public void wrapTextButtonClicked() { wrapText = !wrapText; view.wrapText(wrapText); view.toggleWrapTextButton(wrapText); } @Override public void scrollToBottomButtonClicked() { followOutput = !followOutput; view.toggleScrollToEndButton(followOutput); view.enableAutoScroll(followOutput); } @Override public void onOutputScrolled(boolean bottomReached) { followOutput = bottomReached; view.toggleScrollToEndButton(bottomReached); } /** * Returns the console text. * * @return * console text */ public String getText() { return view.getText(); } }