added env variable about runnerw.exe location
[idea/community.git] / platform / platform-impl / src / com / intellij / execution / process / RunnerMediator.java
1 /*
2  * Copyright 2000-2010 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.configurations.GeneralCommandLine;
20 import com.intellij.openapi.diagnostic.Logger;
21 import com.intellij.openapi.util.SystemInfo;
22 import org.jetbrains.annotations.NotNull;
23 import org.jetbrains.annotations.Nullable;
24
25 import java.io.File;
26 import java.io.OutputStream;
27 import java.io.PrintWriter;
28
29 /**
30  * @author traff
31  */
32 public class RunnerMediator {
33   public static final Logger LOG = Logger.getInstance("#com.intellij.execution.process.RunnerMediator");
34
35   private static final char IAC = (char)5;
36   private static final char BRK = (char)3;
37   private static final String STANDARD_RUNNERW = "runnerw.exe";
38
39   /**
40    * Creates default runner mediator
41    * @return
42    */
43   public static RunnerMediator getInstance() {
44     return new RunnerMediator();
45   }
46
47   /**
48    * Sends sequence of two chars(codes 5 and 3) to a process output stream
49    */
50   private static void sendCtrlBreakThroughStream(Process process) {
51     OutputStream os = process.getOutputStream();
52     PrintWriter pw = new PrintWriter(os);
53     try {
54       pw.print(IAC);
55       pw.print(BRK);
56       pw.flush();
57     }
58     finally {
59       pw.close();
60     }
61   }
62
63   /**
64    * In case of windows creates process with runner mediator(runnerw.exe) injected to command line string, which adds a capability
65    * to terminate process tree gracefully with ctrl+break.
66    *
67    * Returns appropriate process handle, which in case of Unix is able to terminate whole process tree by sending sig_kill
68    *
69    */
70   public ProcessHandler createProcess(@NotNull GeneralCommandLine commandLine) throws ExecutionException {
71     if (isWindows()) {
72       injectRunnerCommand(commandLine);
73     }
74
75     Process process = commandLine.createProcess();
76
77     return createProcessHandler(process, commandLine);
78   }
79
80   /**
81    * Creates process handler for process able to be terminated with method RunnerMediator.destroyProcess.
82    * You can override this method to customize process handler creation.
83    * @return
84    */
85   protected ProcessHandler createProcessHandler(@NotNull Process process, @NotNull GeneralCommandLine commandLine) {
86     return new CustomDestroyProcessHandler(process, commandLine);
87   }
88
89   @Nullable
90   private String getRunnerPath() {
91     if (isWindows()) {
92       final String path = System.getenv("IDEA_RUNNERW");
93       if (path != null && new File(path).exists()) {
94         return path;
95       }
96       if (new File(STANDARD_RUNNERW).exists()) {
97         return STANDARD_RUNNERW;
98       }
99       return null;
100     }
101     else {
102       throw new IllegalStateException("There is no need of runner under unix based OS");
103     }
104   }
105
106   private void injectRunnerCommand(@NotNull GeneralCommandLine commandLine) {
107     final String path = getRunnerPath();
108     if (path != null) {
109       commandLine.getParametersList().addAt(0, commandLine.getExePath());
110       commandLine.setExePath(path);
111     }
112   }
113
114   public static boolean isUnix() {
115     return SystemInfo.isLinux || SystemInfo.isMac;
116   }
117
118   public static boolean isWindows() {
119     if (File.separatorChar == '\\') {
120       return true;
121     }
122     return false;
123   }
124
125   /**
126    * Destroys process tree: in case of windows via imitating ctrl+break, in case of unix via sending sig_kill to every process in tree.
127    * @param process to kill with all subprocesses
128    */
129   public static boolean destroyProcess(Process process) {
130     try {
131       if (isWindows()) {
132         sendCtrlBreakThroughStream(process);
133         return true;
134       }
135       else if (isUnix()) {
136         return UnixProcessManager.sendSigKillToProcessTree(process);
137       }
138       else {
139         return false;
140       }
141     }
142     catch (Exception e) {
143       LOG.error("Couldn't terminate the process", e);
144       return false;
145     }
146   }
147
148   /**
149    *
150    */
151   public static class CustomDestroyProcessHandler extends ColoredProcessHandler {
152
153
154     public CustomDestroyProcessHandler(@NotNull Process process,
155                                        @NotNull GeneralCommandLine commandLine) {
156       super(process, commandLine.getCommandLineString());
157     }
158
159     @Override
160     protected void destroyProcessImpl() {
161       if (!RunnerMediator.destroyProcess(getProcess())) {
162         super.destroyProcessImpl();
163       }
164     }
165   }
166 }