util: add the final modifier to myMediatedProcess
[idea/community.git] / platform / platform-impl / src / com / intellij / execution / process / KillableColoredProcessHandler.java
1 /*
2  * Copyright 2000-2015 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.execution.process;
17
18 import com.intellij.execution.ExecutionException;
19 import com.intellij.execution.KillableProcess;
20 import com.intellij.execution.configurations.GeneralCommandLine;
21 import com.intellij.openapi.diagnostic.Logger;
22 import com.intellij.openapi.util.Key;
23 import com.intellij.openapi.util.SystemInfo;
24 import org.jetbrains.annotations.NotNull;
25
26 import java.io.IOException;
27 import java.nio.charset.Charset;
28
29 /**
30  * This process handler supports ANSI coloring and "soft-kill" feature.
31  * At first "stop" button send SIGINT signal to process, if it still hangs user can terminate it recursively with SIGKILL signal.
32  * <p>
33  * Soft kill works on Unix, and also on Windows if a mediator process was used.
34  *
35  * @author Roman.Chernyatchik
36  */
37 public class KillableColoredProcessHandler extends ColoredProcessHandler implements KillableProcess {
38   private static final Logger LOG = Logger.getInstance(KillableColoredProcessHandler.class);
39   private static final Key<Boolean> MEDIATOR_KEY = Key.create("KillableColoredProcessHandler.Mediator.Process");
40
41   private boolean myShouldKillProcessSoftly = true;
42   private final boolean myMediatedProcess;
43
44   public KillableColoredProcessHandler(@NotNull GeneralCommandLine commandLine) throws ExecutionException {
45     this(commandLine, false);
46   }
47
48   /**
49    * Starts a process with a {@link RunnerMediator mediator} when {@code withMediator} is set to {@code true} and the platform is Windows.
50    */
51   public KillableColoredProcessHandler(@NotNull GeneralCommandLine commandLine, boolean withMediator) throws ExecutionException {
52     super(mediate(commandLine, withMediator));
53     myMediatedProcess = withMediator && MEDIATOR_KEY.get(commandLine) == Boolean.TRUE;
54   }
55
56   /**
57    * {@code commandLine} must not be not empty (for correct thread attribution in the stacktrace)
58    */
59   public KillableColoredProcessHandler(@NotNull Process process, /*@NotNull*/ String commandLine) {
60     super(process, commandLine);
61     myMediatedProcess = false;
62   }
63
64   /**
65    * {@code commandLine} must not be not empty (for correct thread attribution in the stacktrace)
66    */
67   public KillableColoredProcessHandler(@NotNull Process process, /*@NotNull*/ String commandLine, @NotNull Charset charset) {
68     super(process, commandLine, charset);
69     myMediatedProcess = false;
70   }
71
72   @NotNull
73   private static GeneralCommandLine mediate(@NotNull GeneralCommandLine commandLine, boolean withMediator) {
74     if (withMediator && SystemInfo.isWindows) {
75       boolean mediatorInjected = RunnerMediator.injectRunnerCommand(commandLine);
76       MEDIATOR_KEY.set(commandLine, mediatorInjected);
77     }
78     return commandLine;
79   }
80
81   /**
82    * @return true, if graceful process termination should be attempted first
83    */
84   protected boolean shouldKillProcessSoftly() {
85     return myShouldKillProcessSoftly;
86   }
87
88   /**
89    * Sets whether the process will be terminated gracefully.
90    *
91    * @param shouldKillProcessSoftly true, if graceful process termination should be attempted first (i.e. soft kill)
92    */
93   public void setShouldKillProcessSoftly(boolean shouldKillProcessSoftly) {
94     myShouldKillProcessSoftly = shouldKillProcessSoftly;
95   }
96
97   /**
98    * This method shouldn't be overridden, see shouldKillProcessSoftly
99    */
100   private boolean canKillProcessSoftly() {
101     if (processCanBeKilledByOS(myProcess)) {
102       if (SystemInfo.isWindows) {
103         // runnerw.exe can send Ctrl+C events to a wrapped process
104         return myMediatedProcess;
105       }
106       else if (SystemInfo.isUnix) {
107         // 'kill -SIGINT <pid>' will be executed
108         return true;
109       }
110     }
111     return false;
112   }
113
114   @Override
115   protected void destroyProcessImpl() {
116     // Don't close streams, because a process may survive graceful termination.
117     // Streams will be closed after the process is really terminated.
118     try {
119       myProcess.getOutputStream().flush();
120     }
121     catch (IOException e) {
122       LOG.warn(e);
123     }
124     finally {
125       doDestroyProcess();
126     }
127   }
128
129   @Override
130   protected void notifyProcessTerminated(int exitCode) {
131     try {
132       super.closeStreams();
133     }
134     finally {
135       super.notifyProcessTerminated(exitCode);
136     }
137   }
138
139   @Override
140   protected void doDestroyProcess() {
141     boolean gracefulTerminationAttempted = shouldKillProcessSoftly() && canKillProcessSoftly() && destroyProcessGracefully();
142     if (!gracefulTerminationAttempted) {
143       // execute default process destroy
144       super.doDestroyProcess();
145     }
146   }
147
148   protected boolean destroyProcessGracefully() {
149     if (SystemInfo.isWindows && myMediatedProcess) {
150       return RunnerMediator.destroyProcess(myProcess, true);
151     }
152     else if (SystemInfo.isUnix) {
153       return UnixProcessManager.sendSigIntToProcessTree(myProcess);
154     }
155     return false;
156   }
157
158   @Override
159   public boolean canKillProcess() {
160     return processCanBeKilledByOS(getProcess());
161   }
162
163   @Override
164   public void killProcess() {
165     // execute 'kill -SIGKILL <pid>' on Unix
166     killProcessTree(getProcess());
167   }
168
169   /** @deprecated use {@link #KillableColoredProcessHandler(GeneralCommandLine, boolean)} (to be removed in IDEA 17) */
170   @SuppressWarnings("unused")
171   public static KillableColoredProcessHandler create(@NotNull GeneralCommandLine commandLine) throws ExecutionException {
172     return new KillableColoredProcessHandler(commandLine, true);
173   }
174 }