blob: 0b8f32964d596284ef0183b34df46366a805d007 [file] [log] [blame]
/*
* Copyright 2000-2012 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.execution.ExecutionBundle;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.ExecutionResult;
import com.intellij.execution.RunnerRegistry;
import com.intellij.execution.configurations.*;
import com.intellij.execution.executors.DefaultRunExecutor;
import com.intellij.execution.process.CapturingProcessAdapter;
import com.intellij.execution.process.ProcessAdapter;
import com.intellij.execution.process.ProcessEvent;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.execution.runners.*;
import com.intellij.execution.ui.ExecutionConsole;
import com.intellij.execution.ui.RunContentDescriptor;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.project.Project;
import com.intellij.unscramble.AnalyzeStacktraceUtil;
import com.intellij.unscramble.ThreadDumpConsoleFactory;
import com.intellij.unscramble.ThreadDumpParser;
import com.intellij.unscramble.ThreadState;
import com.intellij.util.text.DateFormatUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.util.List;
/**
* @author spleaner
*/
public class DefaultJavaProgramRunner extends JavaPatchableProgramRunner {
private final static String ourWiseThreadDumpProperty = "idea.java.run.wise.thread.dump";
@NonNls public static final String DEFAULT_JAVA_RUNNER_ID = "Run";
@Override
public boolean canRun(@NotNull final String executorId, @NotNull final RunProfile profile) {
return executorId.equals(DefaultRunExecutor.EXECUTOR_ID) &&
profile instanceof ModuleRunProfile &&
!(profile instanceof RunConfigurationWithSuppressedDefaultRunAction);
}
@Override
public void patch(JavaParameters javaParameters, RunnerSettings settings, RunProfile runProfile, final boolean beforeExecution) throws ExecutionException {
runCustomPatchers(javaParameters, DefaultRunExecutor.getRunExecutorInstance(), runProfile);
}
@Override
protected RunContentDescriptor doExecute(@NotNull final RunProfileState state, @NotNull final ExecutionEnvironment env) throws ExecutionException {
FileDocumentManager.getInstance().saveAllDocuments();
ExecutionResult executionResult;
boolean shouldAddDefaultActions = true;
if (state instanceof JavaCommandLine) {
final JavaParameters parameters = ((JavaCommandLine)state).getJavaParameters();
patch(parameters, env.getRunnerSettings(), env.getRunProfile(), true);
final ProcessProxy proxy = ProcessProxyFactory.getInstance().createCommandLineProxy((JavaCommandLine)state);
executionResult = state.execute(env.getExecutor(), this);
if (proxy != null && executionResult != null) {
proxy.attach(executionResult.getProcessHandler());
}
if (state instanceof JavaCommandLineState && !((JavaCommandLineState)state).shouldAddJavaProgramRunnerActions()) {
shouldAddDefaultActions = false;
}
}
else {
executionResult = state.execute(env.getExecutor(), this);
}
if (executionResult == null) {
return null;
}
onProcessStarted(env.getRunnerSettings(), executionResult);
final RunContentBuilder contentBuilder = new RunContentBuilder(executionResult, env);
if (shouldAddDefaultActions) {
addDefaultActions(contentBuilder);
}
return contentBuilder.showRunContent(env.getContentToReuse());
}
@Deprecated
/**
* @deprecated to remove in IDEA 14
*/
protected AnAction[] createActions(@SuppressWarnings("UnusedParameters") final ExecutionResult executionResult) {
return AnAction.EMPTY_ARRAY;
}
protected static void addDefaultActions(final RunContentBuilder contentBuilder) {
final ExecutionResult executionResult = contentBuilder.getExecutionResult();
final ExecutionConsole executionConsole = executionResult.getExecutionConsole();
final JComponent consoleComponent = executionConsole != null ? executionConsole.getComponent() : null;
final ControlBreakAction controlBreakAction = new ControlBreakAction(executionResult.getProcessHandler());
if (consoleComponent != null) {
controlBreakAction.registerCustomShortcutSet(controlBreakAction.getShortcutSet(), consoleComponent);
final ProcessHandler processHandler = executionResult.getProcessHandler();
assert processHandler != null : executionResult;
processHandler.addProcessListener(new ProcessAdapter() {
@Override
public void processTerminated(final ProcessEvent event) {
processHandler.removeProcessListener(this);
controlBreakAction.unregisterCustomShortcutSet(consoleComponent);
}
});
}
contentBuilder.addAction(controlBreakAction);
contentBuilder.addAction(new SoftExitAction(executionResult.getProcessHandler()));
}
private abstract static class LauncherBasedAction extends AnAction {
protected final ProcessHandler myProcessHandler;
protected LauncherBasedAction(String text, String description, Icon icon, ProcessHandler processHandler) {
super(text, description, icon);
myProcessHandler = processHandler;
}
@Override
public void update(@NotNull final AnActionEvent event) {
final Presentation presentation = event.getPresentation();
if (!isVisible()) {
presentation.setVisible(false);
presentation.setEnabled(false);
return;
}
presentation.setVisible(true);
presentation.setEnabled(!myProcessHandler.isProcessTerminated());
}
protected boolean isVisible() {
return ProcessProxyFactory.getInstance().getAttachedProxy(myProcessHandler) != null;
}
}
protected static class ControlBreakAction extends LauncherBasedAction {
public ControlBreakAction(final ProcessHandler processHandler) {
super(ExecutionBundle.message("run.configuration.dump.threads.action.name"), null, AllIcons.Actions.Dump,
processHandler);
setShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_CANCEL, InputEvent.CTRL_DOWN_MASK)));
}
@Override
protected boolean isVisible() {
return super.isVisible() && ProcessProxyFactory.getInstance().isBreakGenLibraryAvailable();
}
@Override
public void actionPerformed(@NotNull final AnActionEvent e) {
ProcessProxy proxy = ProcessProxyFactory.getInstance().getAttachedProxy(myProcessHandler);
if (proxy != null) {
final WiseDumpThreadsListener wiseListener = Boolean.TRUE.equals(Boolean.getBoolean(ourWiseThreadDumpProperty)) ?
new WiseDumpThreadsListener(CommonDataKeys.PROJECT.getData(e.getDataContext()), myProcessHandler) : null;
proxy.sendBreak();
if (wiseListener != null) {
wiseListener.after();
}
}
}
}
private static class WiseDumpThreadsListener {
private final Project myProject;
private final ProcessHandler myProcessHandler;
private final CapturingProcessAdapter myListener;
public WiseDumpThreadsListener(final Project project, final ProcessHandler processHandler) {
myProject = project;
myProcessHandler = processHandler;
myListener = new CapturingProcessAdapter();
myProcessHandler.addProcessListener(myListener);
}
public void after() {
if (myProject == null) {
myProcessHandler.removeProcessListener(myListener);
return;
}
ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
@Override
public void run() {
if (myProcessHandler.isProcessTerminated() || myProcessHandler.isProcessTerminating()) return;
List<ThreadState> threadStates = null;
final long start = System.currentTimeMillis();
while ((System.currentTimeMillis() - start) < 1000) {
final String stdout = myListener.getOutput().getStdout();
threadStates = ThreadDumpParser.parse(stdout);
if (threadStates == null || threadStates.isEmpty()) {
try {
//noinspection BusyWait
Thread.sleep(50);
}
catch (InterruptedException ignored) {
//
}
threadStates = null;
continue;
}
break;
}
myProcessHandler.removeProcessListener(myListener);
if (threadStates != null && ! threadStates.isEmpty()) {
showThreadDump(myListener.getOutput().getStdout(), threadStates);
}
}
});
}
private void showThreadDump(final String out, final List<ThreadState> threadStates) {
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
AnalyzeStacktraceUtil.addConsole(myProject, threadStates.size() > 1 ?
new ThreadDumpConsoleFactory(myProject, threadStates) : null,
"<Stacktrace> " + DateFormatUtil.formatDateTime(System.currentTimeMillis()), out);
}
}, ModalityState.NON_MODAL);
}
}
protected static class SoftExitAction extends LauncherBasedAction {
public SoftExitAction(final ProcessHandler processHandler) {
super(ExecutionBundle.message("run.configuration.exit.action.name"), null, AllIcons.Actions.Exit, processHandler);
}
@Override
public void actionPerformed(@NotNull final AnActionEvent e) {
ProcessProxy proxy = ProcessProxyFactory.getInstance().getAttachedProxy(myProcessHandler);
if (proxy != null) {
proxy.sendStop();
}
}
}
@Override
@NotNull
public String getRunnerId() {
return DEFAULT_JAVA_RUNNER_ID;
}
public static ProgramRunner getInstance() {
return RunnerRegistry.getInstance().findRunnerById(DEFAULT_JAVA_RUNNER_ID);
}
}