| /* |
| * 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); |
| } |
| } |