com.intellij.execution.impl.ExecutionManagerImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.intellij.execution.impl.ExecutionManagerImpl.java

Source

/*
 * Copyright 2000-2014 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.intellij.execution.impl;

import com.intellij.CommonBundle;
import com.intellij.execution.*;
import com.intellij.execution.configuration.CompatibilityAwareRunProfile;
import com.intellij.execution.configurations.RunConfiguration;
import com.intellij.execution.configurations.RunProfile;
import com.intellij.execution.configurations.RunProfileState;
import com.intellij.execution.process.ProcessAdapter;
import com.intellij.execution.process.ProcessEvent;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.execution.runners.ExecutionEnvironmentBuilder;
import com.intellij.execution.runners.ExecutionUtil;
import com.intellij.execution.runners.ProgramRunner;
import com.intellij.execution.ui.RunContentDescriptor;
import com.intellij.execution.ui.RunContentManager;
import com.intellij.execution.ui.RunContentManagerImpl;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.impl.SimpleDataContext;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Trinity;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.ui.docking.DockManager;
import com.intellij.util.Alarm;
import com.intellij.util.SmartList;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class ExecutionManagerImpl extends ExecutionManager implements Disposable {
    public static final Key<Object> EXECUTION_SESSION_ID_KEY = Key.create("EXECUTION_SESSION_ID_KEY");

    private static final Logger LOG = Logger.getInstance(ExecutionManagerImpl.class);
    private static final ProcessHandler[] EMPTY_PROCESS_HANDLERS = new ProcessHandler[0];

    private final Project myProject;

    private RunContentManagerImpl myContentManager;
    private final Alarm awaitingTerminationAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD);
    private final List<Trinity<RunContentDescriptor, RunnerAndConfigurationSettings, Executor>> myRunningConfigurations = ContainerUtil
            .createLockFreeCopyOnWriteList();

    @SuppressWarnings("MethodOverridesStaticMethodOfSuperclass")
    @NotNull
    public static ExecutionManagerImpl getInstance(@NotNull Project project) {
        return (ExecutionManagerImpl) ServiceManager.getService(project, ExecutionManager.class);
    }

    ExecutionManagerImpl(@NotNull Project project) {
        myProject = project;
    }

    @Override
    public void dispose() {
        for (Trinity<RunContentDescriptor, RunnerAndConfigurationSettings, Executor> trinity : myRunningConfigurations) {
            Disposer.dispose(trinity.first);
        }
        myRunningConfigurations.clear();
    }

    @NotNull
    @Override
    public RunContentManager getContentManager() {
        if (myContentManager == null) {
            myContentManager = new RunContentManagerImpl(myProject, DockManager.getInstance(myProject));
            Disposer.register(myProject, myContentManager);
        }
        return myContentManager;
    }

    @NotNull
    @Override
    public ProcessHandler[] getRunningProcesses() {
        if (myContentManager == null)
            return EMPTY_PROCESS_HANDLERS;
        List<ProcessHandler> handlers = null;
        for (RunContentDescriptor descriptor : getContentManager().getAllDescriptors()) {
            ProcessHandler processHandler = descriptor.getProcessHandler();
            if (processHandler != null) {
                if (handlers == null) {
                    handlers = new SmartList<ProcessHandler>();
                }
                handlers.add(processHandler);
            }
        }
        return handlers == null ? EMPTY_PROCESS_HANDLERS : handlers.toArray(new ProcessHandler[handlers.size()]);
    }

    @Override
    public void compileAndRun(@NotNull final Runnable startRunnable,
            @NotNull final ExecutionEnvironment environment, @Nullable final RunProfileState state,
            @Nullable final Runnable onCancelRunnable) {
        long id = environment.getExecutionId();
        if (id == 0) {
            id = environment.assignNewExecutionId();
        }

        RunProfile profile = environment.getRunProfile();
        if (!(profile instanceof RunConfiguration)) {
            startRunnable.run();
            return;
        }

        final RunConfiguration runConfiguration = (RunConfiguration) profile;
        final List<BeforeRunTask> beforeRunTasks = RunManagerEx.getInstanceEx(myProject)
                .getBeforeRunTasks(runConfiguration);
        if (beforeRunTasks.isEmpty()) {
            startRunnable.run();
        } else {
            DataContext context = environment.getDataContext();
            final DataContext projectContext = context != null ? context
                    : SimpleDataContext.getProjectContext(myProject);
            final long finalId = id;
            final Long executionSessionId = new Long(id);
            ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
                /** @noinspection SSBasedInspection*/
                @Override
                public void run() {
                    for (BeforeRunTask task : beforeRunTasks) {
                        if (myProject.isDisposed()) {
                            return;
                        }
                        @SuppressWarnings("unchecked")
                        BeforeRunTaskProvider<BeforeRunTask> provider = BeforeRunTaskProvider.getProvider(myProject,
                                task.getProviderId());
                        if (provider == null) {
                            LOG.warn("Cannot find BeforeRunTaskProvider for id='" + task.getProviderId() + "'");
                            continue;
                        }
                        ExecutionEnvironment taskEnvironment = new ExecutionEnvironmentBuilder(environment)
                                .contentToReuse(null).build();
                        taskEnvironment.setExecutionId(finalId);
                        EXECUTION_SESSION_ID_KEY.set(taskEnvironment, executionSessionId);
                        if (!provider.executeTask(projectContext, runConfiguration, taskEnvironment, task)) {
                            if (onCancelRunnable != null) {
                                SwingUtilities.invokeLater(onCancelRunnable);
                            }
                            return;
                        }
                    }
                    // important! Do not use DumbService.smartInvokeLater here because it depends on modality state
                    // and execution of startRunnable could be skipped if modality state check fails
                    SwingUtilities.invokeLater(new Runnable() {
                        @Override
                        public void run() {
                            if (!myProject.isDisposed()) {
                                DumbService.getInstance(myProject).runWhenSmart(startRunnable);
                            }
                        }
                    });
                }
            });
        }
    }

    @Override
    public void startRunProfile(@NotNull final RunProfileStarter starter, @NotNull final RunProfileState state,
            @NotNull final ExecutionEnvironment environment) {
        final Project project = environment.getProject();
        final RunContentDescriptor reuseContent = getContentManager().getReuseContent(environment);
        if (reuseContent != null) {
            reuseContent.setExecutionId(environment.getExecutionId());
        }

        final Executor executor = environment.getExecutor();
        project.getMessageBus().syncPublisher(EXECUTION_TOPIC).processStartScheduled(executor.getId(), environment);

        Runnable startRunnable = new Runnable() {
            @Override
            public void run() {
                if (project.isDisposed()) {
                    return;
                }

                RunProfile profile = environment.getRunProfile();
                boolean started = false;
                try {
                    project.getMessageBus().syncPublisher(EXECUTION_TOPIC).processStarting(executor.getId(),
                            environment);

                    final RunContentDescriptor descriptor = starter.execute(project, executor, state, reuseContent,
                            environment);
                    if (descriptor != null) {
                        environment.setContentToReuse(descriptor);
                        final Trinity<RunContentDescriptor, RunnerAndConfigurationSettings, Executor> trinity = Trinity
                                .create(descriptor, environment.getRunnerAndConfigurationSettings(), executor);
                        myRunningConfigurations.add(trinity);
                        Disposer.register(descriptor, new Disposable() {
                            @Override
                            public void dispose() {
                                myRunningConfigurations.remove(trinity);
                            }
                        });
                        getContentManager().showRunContent(executor, descriptor, reuseContent);
                        final ProcessHandler processHandler = descriptor.getProcessHandler();
                        if (processHandler != null) {
                            if (!processHandler.isStartNotified()) {
                                processHandler.startNotify();
                            }
                            project.getMessageBus().syncPublisher(EXECUTION_TOPIC).processStarted(executor.getId(),
                                    environment, processHandler);
                            started = true;
                            processHandler.addProcessListener(
                                    new ProcessExecutionListener(project, profile, processHandler));
                        }
                    }
                } catch (ExecutionException e) {
                    ExecutionUtil.handleExecutionError(project, executor.getToolWindowId(), profile, e);
                    LOG.info(e);
                } finally {
                    if (!started) {
                        project.getMessageBus().syncPublisher(EXECUTION_TOPIC).processNotStarted(executor.getId(),
                                environment);
                    }
                }
            }
        };

        if (ApplicationManager.getApplication().isUnitTestMode()) {
            startRunnable.run();
        } else {
            compileAndRun(startRunnable, environment, state, new Runnable() {
                @Override
                public void run() {
                    if (!project.isDisposed()) {
                        project.getMessageBus().syncPublisher(EXECUTION_TOPIC).processNotStarted(executor.getId(),
                                environment);
                    }
                }
            });
        }
    }

    @Override
    public void restartRunProfile(@NotNull Project project, @NotNull Executor executor,
            @NotNull ExecutionTarget target, @Nullable RunnerAndConfigurationSettings configuration,
            @Nullable ProcessHandler processHandler) {
        ExecutionEnvironmentBuilder builder = createEnvironmentBuilder(project, executor, configuration);
        if (processHandler != null) {
            for (RunContentDescriptor descriptor : getContentManager().getAllDescriptors()) {
                if (descriptor.getProcessHandler() == processHandler) {
                    builder.contentToReuse(descriptor);
                    break;
                }
            }
        }
        restartRunProfile(builder.target(target).build());
    }

    @NotNull
    private static ExecutionEnvironmentBuilder createEnvironmentBuilder(@NotNull Project project,
            @NotNull Executor executor, @Nullable RunnerAndConfigurationSettings configuration) {
        ExecutionEnvironmentBuilder builder = new ExecutionEnvironmentBuilder(project, executor);

        ProgramRunner runner = RunnerRegistry.getInstance().getRunner(executor.getId(),
                configuration != null ? configuration.getConfiguration() : null);
        if (runner == null && configuration != null) {
            LOG.error("Cannot find runner for " + configuration.getName());
        } else if (runner != null) {
            assert configuration != null;
            builder.runnerAndSettings(runner, configuration);
        }
        return builder;
    }

    @Override
    public void restartRunProfile(@NotNull Project project, @NotNull Executor executor,
            @NotNull ExecutionTarget target, @Nullable RunnerAndConfigurationSettings configuration,
            @Nullable RunContentDescriptor currentDescriptor) {
        ExecutionEnvironmentBuilder builder = createEnvironmentBuilder(project, executor, configuration);
        restartRunProfile(builder.target(target).contentToReuse(currentDescriptor).build());
    }

    @Override
    public void restartRunProfile(@Nullable ProgramRunner runner, @NotNull ExecutionEnvironment environment,
            @Nullable RunContentDescriptor currentDescriptor) {
        ExecutionEnvironmentBuilder builder = new ExecutionEnvironmentBuilder(environment)
                .contentToReuse(currentDescriptor);
        if (runner != null) {
            builder.runner(runner);
        }
        restartRunProfile(builder.build());
    }

    public static boolean isProcessRunning(@Nullable RunContentDescriptor descriptor) {
        ProcessHandler processHandler = descriptor == null ? null : descriptor.getProcessHandler();
        return processHandler != null && !processHandler.isProcessTerminated();
    }

    @Override
    public void restartRunProfile(@NotNull final ExecutionEnvironment environment) {
        RunnerAndConfigurationSettings configuration = environment.getRunnerAndConfigurationSettings();

        List<RunContentDescriptor> runningIncompatible;
        if (configuration == null) {
            runningIncompatible = Collections.emptyList();
        } else {
            runningIncompatible = getIncompatibleRunningDescriptors(configuration);
        }

        RunContentDescriptor contentToReuse = environment.getContentToReuse();
        final List<RunContentDescriptor> runningOfTheSameType = new SmartList<RunContentDescriptor>();
        if (configuration != null && configuration.isSingleton()) {
            runningOfTheSameType.addAll(getRunningDescriptorsOfTheSameConfigType(configuration));
        } else if (isProcessRunning(contentToReuse)) {
            runningOfTheSameType.add(contentToReuse);
        }

        List<RunContentDescriptor> runningToStop = ContainerUtil.concat(runningOfTheSameType, runningIncompatible);
        if (!runningToStop.isEmpty()) {
            if (configuration != null) {
                if (!runningOfTheSameType.isEmpty()
                        && (runningOfTheSameType.size() > 1 || contentToReuse == null
                                || runningOfTheSameType.get(0) != contentToReuse)
                        && !userApprovesStopForSameTypeConfigurations(environment.getProject(),
                                configuration.getName(), runningOfTheSameType.size())) {
                    return;
                }
                if (!runningIncompatible.isEmpty() && !userApprovesStopForIncompatibleConfigurations(myProject,
                        configuration.getName(), runningIncompatible)) {
                    return;
                }
            }

            for (RunContentDescriptor descriptor : runningToStop) {
                stop(descriptor);
            }
        }

        awaitingTerminationAlarm.addRequest(new Runnable() {
            @Override
            public void run() {
                if (ExecutorRegistry.getInstance().isStarting(environment)) {
                    awaitingTerminationAlarm.addRequest(this, 100);
                    return;
                }

                for (RunContentDescriptor descriptor : runningOfTheSameType) {
                    ProcessHandler processHandler = descriptor.getProcessHandler();
                    if (processHandler != null && !processHandler.isProcessTerminated()) {
                        awaitingTerminationAlarm.addRequest(this, 100);
                        return;
                    }
                }
                start(environment);
            }
        }, 50);
    }

    private static void start(@NotNull ExecutionEnvironment environment) {
        RunnerAndConfigurationSettings settings = environment.getRunnerAndConfigurationSettings();
        ProgramRunnerUtil.executeConfiguration(environment, settings != null && settings.isEditBeforeRun(), true);
    }

    private static boolean userApprovesStopForSameTypeConfigurations(Project project, String configName,
            int instancesCount) {
        RunManagerImpl runManager = RunManagerImpl.getInstanceImpl(project);
        final RunManagerConfig config = runManager.getConfig();
        if (!config.isRestartRequiresConfirmation())
            return true;

        DialogWrapper.DoNotAskOption option = new DialogWrapper.DoNotAskOption() {
            @Override
            public boolean isToBeShown() {
                return config.isRestartRequiresConfirmation();
            }

            @Override
            public void setToBeShown(boolean value, int exitCode) {
                config.setRestartRequiresConfirmation(value);
            }

            @Override
            public boolean canBeHidden() {
                return true;
            }

            @Override
            public boolean shouldSaveOptionsOnCancel() {
                return false;
            }

            @NotNull
            @Override
            public String getDoNotShowMessage() {
                return CommonBundle.message("dialog.options.do.not.show");
            }
        };
        return Messages.showOkCancelDialog(project,
                ExecutionBundle.message("rerun.singleton.confirmation.message", configName, instancesCount),
                ExecutionBundle.message("process.is.running.dialog.title", configName),
                ExecutionBundle.message("rerun.confirmation.button.text"), CommonBundle.message("button.cancel"),
                Messages.getQuestionIcon(), option) == Messages.OK;
    }

    private static boolean userApprovesStopForIncompatibleConfigurations(Project project, String configName,
            List<RunContentDescriptor> runningIncompatibleDescriptors) {
        RunManagerImpl runManager = RunManagerImpl.getInstanceImpl(project);
        final RunManagerConfig config = runManager.getConfig();
        if (!config.isRestartRequiresConfirmation())
            return true;

        DialogWrapper.DoNotAskOption option = new DialogWrapper.DoNotAskOption() {
            @Override
            public boolean isToBeShown() {
                return config.isRestartRequiresConfirmation();
            }

            @Override
            public void setToBeShown(boolean value, int exitCode) {
                config.setRestartRequiresConfirmation(value);
            }

            @Override
            public boolean canBeHidden() {
                return true;
            }

            @Override
            public boolean shouldSaveOptionsOnCancel() {
                return false;
            }

            @NotNull
            @Override
            public String getDoNotShowMessage() {
                return CommonBundle.message("dialog.options.do.not.show");
            }
        };

        final StringBuilder names = new StringBuilder();
        for (final RunContentDescriptor descriptor : runningIncompatibleDescriptors) {
            String name = descriptor.getDisplayName();
            if (names.length() > 0) {
                names.append(", ");
            }
            names.append(StringUtil.isEmpty(name) ? ExecutionBundle.message("run.configuration.no.name")
                    : String.format("'%s'", name));
        }

        //noinspection DialogTitleCapitalization
        return Messages.showOkCancelDialog(project,
                ExecutionBundle.message("stop.incompatible.confirmation.message", configName, names.toString(),
                        runningIncompatibleDescriptors.size()),
                ExecutionBundle.message("incompatible.configuration.is.running.dialog.title",
                        runningIncompatibleDescriptors.size()),
                ExecutionBundle.message("stop.incompatible.confirmation.button.text"),
                CommonBundle.message("button.cancel"), Messages.getQuestionIcon(), option) == Messages.OK;
    }

    @NotNull
    private List<RunContentDescriptor> getRunningDescriptorsOfTheSameConfigType(
            @NotNull final RunnerAndConfigurationSettings configurationAndSettings) {
        return getRunningDescriptors(new Condition<RunnerAndConfigurationSettings>() {
            @Override
            public boolean value(@Nullable RunnerAndConfigurationSettings runningConfigurationAndSettings) {
                return configurationAndSettings == runningConfigurationAndSettings;
            }
        });
    }

    @NotNull
    private List<RunContentDescriptor> getIncompatibleRunningDescriptors(
            @NotNull RunnerAndConfigurationSettings configurationAndSettings) {
        final RunConfiguration configurationToCheckCompatibility = configurationAndSettings.getConfiguration();
        return getRunningDescriptors(new Condition<RunnerAndConfigurationSettings>() {
            @Override
            public boolean value(@Nullable RunnerAndConfigurationSettings runningConfigurationAndSettings) {
                RunConfiguration runningConfiguration = runningConfigurationAndSettings == null ? null
                        : runningConfigurationAndSettings.getConfiguration();
                if (runningConfiguration == null
                        || !(runningConfiguration instanceof CompatibilityAwareRunProfile)) {
                    return false;
                }
                return ((CompatibilityAwareRunProfile) runningConfiguration)
                        .mustBeStoppedToRun(configurationToCheckCompatibility);
            }
        });
    }

    @NotNull
    public List<RunContentDescriptor> getRunningDescriptors(
            @NotNull Condition<RunnerAndConfigurationSettings> condition) {
        List<RunContentDescriptor> result = new SmartList<RunContentDescriptor>();
        for (Trinity<RunContentDescriptor, RunnerAndConfigurationSettings, Executor> trinity : myRunningConfigurations) {
            if (condition.value(trinity.getSecond())) {
                ProcessHandler processHandler = trinity.getFirst().getProcessHandler();
                if (processHandler != null
                        /*&& !processHandler.isProcessTerminating()*/ && !processHandler.isProcessTerminated()) {
                    result.add(trinity.getFirst());
                }
            }
        }
        return result;
    }

    private static void stop(@Nullable RunContentDescriptor descriptor) {
        ProcessHandler processHandler = descriptor != null ? descriptor.getProcessHandler() : null;
        if (processHandler == null) {
            return;
        }

        if (processHandler instanceof KillableProcess && processHandler.isProcessTerminating()) {
            ((KillableProcess) processHandler).killProcess();
            return;
        }

        if (!processHandler.isProcessTerminated()) {
            if (processHandler.detachIsDefault()) {
                processHandler.detachProcess();
            } else {
                processHandler.destroyProcess();
            }
        }
    }

    @NotNull
    public Set<Executor> getExecutors(RunContentDescriptor descriptor) {
        Set<Executor> result = new HashSet<Executor>();
        for (Trinity<RunContentDescriptor, RunnerAndConfigurationSettings, Executor> trinity : myRunningConfigurations) {
            if (descriptor == trinity.first)
                result.add(trinity.third);
        }
        return result;
    }

    private static class ProcessExecutionListener extends ProcessAdapter {
        private final Project myProject;
        private final RunProfile myProfile;
        private final ProcessHandler myProcessHandler;

        public ProcessExecutionListener(Project project, RunProfile profile, ProcessHandler processHandler) {
            myProject = project;
            myProfile = profile;
            myProcessHandler = processHandler;
        }

        @Override
        public void processTerminated(ProcessEvent event) {
            if (myProject.isDisposed())
                return;

            myProject.getMessageBus().syncPublisher(EXECUTION_TOPIC).processTerminated(myProfile, myProcessHandler);
            VirtualFileManager.getInstance().asyncRefresh(null);
        }

        @Override
        public void processWillTerminate(ProcessEvent event, boolean willBeDestroyed) {
            if (myProject.isDisposed())
                return;

            myProject.getMessageBus().syncPublisher(EXECUTION_TOPIC).processTerminating(myProfile,
                    myProcessHandler);
        }
    }
}