blob: 8b68dcc1ab031fc01f82d9cfa638f2435d66810d [file] [log] [blame]
package com.intellij.openapi.externalSystem.service.execution;
import com.intellij.execution.DefaultExecutionResult;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.ExecutionResult;
import com.intellij.execution.Executor;
import com.intellij.execution.configurations.ConfigurationFactory;
import com.intellij.execution.configurations.LocatableConfigurationBase;
import com.intellij.execution.configurations.RunConfiguration;
import com.intellij.execution.configurations.RunProfileState;
import com.intellij.execution.executors.DefaultDebugExecutor;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.execution.process.ProcessOutputTypes;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.execution.runners.ProgramRunner;
import com.intellij.execution.ui.ExecutionConsole;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.externalSystem.execution.ExternalSystemExecutionConsoleManager;
import com.intellij.openapi.externalSystem.model.ProjectSystemId;
import com.intellij.openapi.externalSystem.model.execution.ExternalSystemTaskExecutionSettings;
import com.intellij.openapi.externalSystem.model.execution.ExternalTaskExecutionInfo;
import com.intellij.openapi.externalSystem.model.execution.ExternalTaskPojo;
import com.intellij.openapi.externalSystem.model.task.ExternalSystemTask;
import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskId;
import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationListenerAdapter;
import com.intellij.openapi.externalSystem.service.internal.ExternalSystemExecuteTaskTask;
import com.intellij.openapi.externalSystem.util.ExternalSystemBundle;
import com.intellij.openapi.externalSystem.util.ExternalSystemUtil;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.options.SettingsEditor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.ExceptionUtil;
import com.intellij.util.containers.ContainerUtilRt;
import com.intellij.util.net.NetUtils;
import com.intellij.util.text.DateFormatUtil;
import com.intellij.util.xmlb.XmlSerializer;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.*;
import java.util.List;
/**
* @author Denis Zhdanov
* @since 23.05.13 18:30
*/
public class ExternalSystemRunConfiguration extends LocatableConfigurationBase {
private static final Logger LOG = Logger.getInstance("#" + ExternalSystemRunConfiguration.class.getName());
private ExternalSystemTaskExecutionSettings mySettings = new ExternalSystemTaskExecutionSettings();
public ExternalSystemRunConfiguration(@NotNull ProjectSystemId externalSystemId,
Project project,
ConfigurationFactory factory,
String name) {
super(project, factory, name);
mySettings.setExternalSystemIdString(externalSystemId.getId());
}
@Override
public String suggestedName() {
return AbstractExternalSystemTaskConfigurationType.generateName(getProject(), mySettings);
}
@Override
public RunConfiguration clone() {
ExternalSystemRunConfiguration result = (ExternalSystemRunConfiguration)super.clone();
result.mySettings = mySettings.clone();
return result;
}
@Override
public void readExternal(Element element) throws InvalidDataException {
super.readExternal(element);
Element e = element.getChild(ExternalSystemTaskExecutionSettings.TAG_NAME);
if (e != null) {
mySettings = XmlSerializer.deserialize(e, ExternalSystemTaskExecutionSettings.class);
}
}
@Override
public void writeExternal(Element element) throws WriteExternalException {
super.writeExternal(element);
element.addContent(XmlSerializer.serialize(mySettings));
}
@NotNull
public ExternalSystemTaskExecutionSettings getSettings() {
return mySettings;
}
@NotNull
@Override
public SettingsEditor<? extends RunConfiguration> getConfigurationEditor() {
return new ExternalSystemRunConfigurationEditor(getProject(), mySettings.getExternalSystemId());
}
@Nullable
@Override
public RunProfileState getState(@NotNull Executor executor, @NotNull ExecutionEnvironment env) throws ExecutionException {
return new MyRunnableState(mySettings, getProject(), DefaultDebugExecutor.EXECUTOR_ID.equals(executor.getId()), this, env);
}
public static class MyRunnableState implements RunProfileState {
@NotNull private final ExternalSystemTaskExecutionSettings mySettings;
@NotNull private final Project myProject;
@NotNull private final ExternalSystemRunConfiguration myConfiguration;
@NotNull private final ExecutionEnvironment myEnv;
private final int myDebugPort;
public MyRunnableState(@NotNull ExternalSystemTaskExecutionSettings settings,
@NotNull Project project,
boolean debug,
@NotNull ExternalSystemRunConfiguration configuration,
@NotNull ExecutionEnvironment env) {
mySettings = settings;
myProject = project;
myConfiguration = configuration;
myEnv = env;
int port;
if (debug) {
try {
port = NetUtils.findAvailableSocketPort();
}
catch (IOException e) {
LOG.warn("Unexpected I/O exception occurred on attempt to find a free port to use for external system task debugging", e);
port = 0;
}
}
else {
port = 0;
}
myDebugPort = port;
}
public int getDebugPort() {
return myDebugPort;
}
@Nullable
@Override
public ExecutionResult execute(Executor executor, @NotNull ProgramRunner runner) throws ExecutionException {
if (myProject.isDisposed()) return null;
ExternalSystemUtil.updateRecentTasks(new ExternalTaskExecutionInfo(mySettings.clone(), executor.getId()), myProject);
final List<ExternalTaskPojo> tasks = ContainerUtilRt.newArrayList();
for (String taskName : mySettings.getTaskNames()) {
tasks.add(new ExternalTaskPojo(taskName, mySettings.getExternalProjectPath(), null));
}
if (tasks.isEmpty()) {
throw new ExecutionException(ExternalSystemBundle.message("run.error.undefined.task"));
}
String debuggerSetup = null;
if (myDebugPort > 0) {
debuggerSetup = "-agentlib:jdwp=transport=dt_socket,server=n,suspend=y,address=" + myDebugPort;
}
ApplicationManager.getApplication().assertIsDispatchThread();
FileDocumentManager.getInstance().saveAllDocuments();
final ExternalSystemExecuteTaskTask task = new ExternalSystemExecuteTaskTask(mySettings.getExternalSystemId(),
myProject,
tasks,
mySettings.getVmOptions(),
mySettings.getScriptParameters(),
debuggerSetup);
final MyProcessHandler processHandler = new MyProcessHandler(task);
final ExternalSystemExecutionConsoleManager<ExternalSystemRunConfiguration> consoleManager = getConsoleManagerFor(task);
final ExecutionConsole consoleView =
consoleManager.attachExecutionConsole(task, myProject, myConfiguration, executor, myEnv, processHandler);
Disposer.register(myProject, consoleView);
ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
@Override
public void run() {
final String startDateTime = DateFormatUtil.formatTimeWithSeconds(System.currentTimeMillis());
final String greeting;
if (mySettings.getTaskNames().size() > 1) {
greeting = ExternalSystemBundle
.message("run.text.starting.multiple.task", startDateTime, StringUtil.join(mySettings.getTaskNames(), " "));
}
else {
greeting =
ExternalSystemBundle.message("run.text.starting.single.task", startDateTime, StringUtil.join(mySettings.getTaskNames(), " "));
}
processHandler.notifyTextAvailable(greeting, ProcessOutputTypes.SYSTEM);
task.execute(new ExternalSystemTaskNotificationListenerAdapter() {
private boolean myResetGreeting = true;
@Override
public void onTaskOutput(@NotNull ExternalSystemTaskId id, @NotNull String text, boolean stdOut) {
if (myResetGreeting) {
processHandler.notifyTextAvailable("\r", ProcessOutputTypes.SYSTEM);
myResetGreeting = false;
}
consoleManager.onOutput(text, stdOut ? ProcessOutputTypes.STDOUT : ProcessOutputTypes.STDERR);
}
@Override
public void onFailure(@NotNull ExternalSystemTaskId id, @NotNull Exception e) {
String exceptionMessage = ExceptionUtil.getMessage(e);
String text = exceptionMessage == null ? e.toString() : exceptionMessage;
processHandler.notifyTextAvailable(text + '\n', ProcessOutputTypes.STDERR);
processHandler.notifyProcessTerminated(1);
}
@Override
public void onEnd(@NotNull ExternalSystemTaskId id) {
final String endDateTime = DateFormatUtil.formatTimeWithSeconds(System.currentTimeMillis());
final String farewell;
if (mySettings.getTaskNames().size() > 1) {
farewell = ExternalSystemBundle
.message("run.text.ended.multiple.task", endDateTime, StringUtil.join(mySettings.getTaskNames(), " "));
}
else {
farewell =
ExternalSystemBundle.message("run.text.ended.single.task", endDateTime, StringUtil.join(mySettings.getTaskNames(), " "));
}
processHandler.notifyTextAvailable(farewell, ProcessOutputTypes.SYSTEM);
processHandler.notifyProcessTerminated(0);
}
});
}
});
DefaultExecutionResult result = new DefaultExecutionResult(consoleView, processHandler);
result.setRestartActions(consoleManager.getRestartActions());
return result;
}
}
private static class MyProcessHandler extends ProcessHandler {
private final ExternalSystemExecuteTaskTask myTask;
@Nullable private volatile OutputStream myOutputStream;
public MyProcessHandler(ExternalSystemExecuteTaskTask task) {
myTask = task;
}
@Override
protected void destroyProcessImpl() {
}
@Override
protected void detachProcessImpl() {
myTask.cancel(new ExternalSystemTaskNotificationListenerAdapter() {
private boolean myResetGreeting = true;
@Override
public void onTaskOutput(@NotNull ExternalSystemTaskId id, @NotNull String text, boolean stdOut) {
if (myResetGreeting) {
notifyTextAvailable("\r", ProcessOutputTypes.SYSTEM);
myResetGreeting = false;
}
notifyTextAvailable(text, stdOut ? ProcessOutputTypes.STDOUT : ProcessOutputTypes.STDERR);
}
});
notifyProcessDetached();
}
@Override
public boolean detachIsDefault() {
return true;
}
@Nullable
@Override
public OutputStream getProcessInput() {
return null;
}
@Override
public void notifyProcessTerminated(int exitCode) {
super.notifyProcessTerminated(exitCode);
}
}
@NotNull
private static ExternalSystemExecutionConsoleManager<ExternalSystemRunConfiguration> getConsoleManagerFor(@NotNull ExternalSystemTask task) {
for (ExternalSystemExecutionConsoleManager executionConsoleManager : ExternalSystemExecutionConsoleManager.EP_NAME.getExtensions()) {
if (executionConsoleManager.isApplicableFor(task))
//noinspection unchecked
return executionConsoleManager;
}
return new DefaultExternalSystemExecutionConsoleManager();
}
}