1 package cz.cuni.amis.pogamut.ut2004.tournament.botexecution;
2
3 import java.io.File;
4 import java.io.IOException;
5 import java.util.ArrayList;
6 import java.util.List;
7 import java.util.Map.Entry;
8 import java.util.logging.Level;
9 import java.util.logging.Logger;
10
11 import cz.cuni.amis.pogamut.ut2004.utils.PogamutUT2004Property;
12 import cz.cuni.amis.utils.StreamSink;
13 import cz.cuni.amis.utils.StringIdifier;
14 import cz.cuni.amis.utils.exception.PogamutException;
15 import cz.cuni.amis.utils.exception.PogamutIOException;
16 import cz.cuni.amis.utils.flag.Flag;
17 import cz.cuni.amis.utils.flag.FlagListener;
18 import cz.cuni.amis.utils.flag.ImmutableFlag;
19
20
21
22
23
24
25
26
27 public class UT2004BotExecution {
28
29 protected UT2004BotExecutionConfig config;
30
31
32
33
34
35
36
37 protected Flag<Boolean> running = new Flag<Boolean>(false);
38
39
40
41
42 protected Process botProcess = null;
43
44
45
46
47 protected StreamSink streamSinkOutput = null;
48
49
50
51
52 protected StreamSink streamSinkError = null;
53
54
55
56
57 protected Logger log;
58
59
60
61
62
63 public UT2004BotExecution(UT2004BotExecutionConfig config, Logger log) {
64 this.log = log;
65 this.config = config;
66 }
67
68
69
70
71 protected Runnable shutDownHook = new Runnable(){
72
73 @Override
74 public void run() {
75 if (botProcess != null) botProcess.destroy();
76 }
77 };
78
79 protected Thread shutDownHookThread;
80
81
82
83
84
85 protected Runnable waitForEnd = new Runnable() {
86
87 @Override
88 public void run() {
89 try {
90 botProcess.waitFor();
91
92 } catch (InterruptedException e) {
93
94 synchronized(running) {
95 if (!running.getFlag()) {
96
97 return;
98 }
99 }
100
101 if (log != null && log.isLoggable(Level.WARNING)) {
102 log.warning("Interrupted while waiting for the botProcess(" + config.getBotId().getToken() + ") to end!");
103 }
104
105 } finally {
106
107 synchronized(running) {
108 if (!running.getFlag()) {
109
110 return;
111 }
112 if (waitForEndThread == Thread.currentThread()) {
113
114 shutdown(true);
115 }
116 }
117 }
118 }
119 };
120
121 protected Thread waitForEndThread;
122
123
124
125
126
127
128
129
130
131
132 public void start(String host, int port) throws PogamutIOException {
133 synchronized(running) {
134 if (running.getFlag()) {
135 throw new PogamutException("Could not start the bot again, it is already running! stop() it first!", log, this);
136 }
137
138 if (log != null && log.isLoggable(Level.WARNING)) {
139 log.warning("Starting bot: " + config);
140 }
141
142 if (!config.isBotJarExist()) {
143 throw new PogamutException("Could not start the bot according to config " + config + " as the bot jar does not exist at specified place " + config.getJarFile().getAbsolutePath() + "!", this);
144 }
145
146 String javaHome = System.getProperty("JAVA_HOME");
147
148 boolean linux = System.getProperty("os.name").toLowerCase().contains("linux");
149 boolean mac = System.getProperty("os.name").contains("Mac");
150
151
152
153 String command =
154 (javaHome == null
155 ? (linux || mac ? "java" : "java.exe")
156 : javaHome + (linux || mac ? "/bin/java" : "\\bin\\java.exe"));
157
158 Object origHost = config.addParameter(PogamutUT2004Property.POGAMUT_UT2004_BOT_HOST.getKey(), host);
159 if (origHost != null) {
160 log.warning("Reconfiguring Bot[id=" + config.getBotId().getToken() + "] parameter " + PogamutUT2004Property.POGAMUT_UT2004_BOT_HOST.getKey() + " from value '" + String.valueOf(origHost) + "' to value '" + host + "'.");
161 }
162 Object origPort = config.addParameter(PogamutUT2004Property.POGAMUT_UT2004_BOT_PORT.getKey(), port);
163 if (origPort != null) {
164 log.warning("Reconfiguring Bot[id=" + config.getBotId().getToken() + "] parameter " + PogamutUT2004Property.POGAMUT_UT2004_BOT_HOST.getKey() + " from value '" + String.valueOf(origPort) + "' to value '" + port + "'.");
165 }
166
167 List<String> commandForProcessBuilder = new ArrayList<String>(3 + config.getParameters().size());
168
169 commandForProcessBuilder.add(command);
170
171 StringBuffer javaParameters = new StringBuffer();
172
173 for (Entry<String, Object> parameter : config.getParameters().entrySet()) {
174 String parameterKey = StringIdifier.idify(parameter.getKey(), ".-_", "_");
175 String parameterValue = String.valueOf(parameter.getValue());
176 if (parameterValue.contains("\"")) {
177 throw new PogamutException("Could not start the bot according to config " + config + " as it contains parameter containing '\"': " + parameterKey + " = " + parameterValue, this);
178 }
179 String param = "-D" + parameterKey + "=" + parameterValue;
180
181 javaParameters.append(" \"" + param + "\"");
182
183 commandForProcessBuilder.add(param);
184 }
185
186 commandForProcessBuilder.add("-jar");
187 commandForProcessBuilder.add(config.getJarFile().getAbsolutePath());
188
189 String fullCommand = command + javaParameters.toString() + " -jar \"" + config.getJarFile().getAbsolutePath() + "\"";
190
191 if (log != null && log.isLoggable(Level.INFO)) {
192 log.info("Executing command: " + fullCommand);
193 }
194
195 ProcessBuilder procBuilder =
196 new ProcessBuilder(
197 commandForProcessBuilder.toArray(new String[commandForProcessBuilder.size()])
198 );
199
200 procBuilder.directory(new File(config.getJarFile().getParent()));
201
202 try {
203 botProcess = procBuilder.start();
204 } catch (IOException e) {
205
206 if (log != null && log.isLoggable(Level.SEVERE)) {
207 log.severe("Could not start the bot: " + e.getMessage());
208 }
209 botProcess = null;
210 throw new PogamutIOException("Failed to start the botProcess(" + config.getBotId().getToken() + "). IOException: " + e.getMessage(), e, this);
211 }
212
213
214 if (config.isRedirectStdErr()) {
215 streamSinkError = new StreamSink(config.getBotId().getToken()+"-StdErrSink", botProcess.getErrorStream(), log, config.getBotId().getToken()+"-StdErr");
216 } else {
217 streamSinkError = new StreamSink(config.getBotId().getToken()+"-StdErrSink", botProcess.getErrorStream());
218 }
219 streamSinkError.start();
220 if (config.isRedirectStdOut()) {
221 streamSinkOutput = new StreamSink(config.getBotId().getToken()+"-StdOutSink", botProcess.getInputStream(), log, config.getBotId().getToken()+"-StdOut");
222 } else {
223 streamSinkOutput = new StreamSink(config.getBotId().getToken()+"-StdOutSink", botProcess.getInputStream());
224 }
225 streamSinkOutput.start();
226 shutDownHookThread = new Thread(shutDownHook, config.getBotId().getToken()+"-JVMShutdownHook");
227 Runtime.getRuntime().addShutdownHook(shutDownHookThread);
228 waitForEndThread = new Thread(waitForEnd, config.getBotId().getToken()+"-WaitForProcessEnd");
229 running.setFlag(true);
230 waitForEndThread.start();
231 }
232 }
233
234
235
236
237
238 protected void shutdown(boolean waitForEndThread) {
239 synchronized(running) {
240 if (!running.getFlag()) {
241
242 return;
243 }
244
245 if (log != null && log.isLoggable(Level.WARNING)) {
246 log.warning("Shutting down botProcess(" + config.getBotId().getToken() + ")!");
247 }
248
249 if (log != null && log.isLoggable(Level.WARNING)) {
250 log.warning("... destroying botProcess(" + config.getBotId().getToken() + ").");
251 }
252 if (botProcess != null) {
253 try {
254 botProcess.destroy();
255 } catch (Exception e) {
256 }
257 }
258 botProcess = null;
259
260 if (log != null && log.isLoggable(Level.WARNING)) {
261 log.warning("... destroying streamSinkError(" + config.getBotId().getToken() + ").");
262 }
263 try {
264 if (streamSinkError != null) streamSinkError.interrupt();
265 } catch (Exception e) {
266 }
267 streamSinkError = null;
268
269 if (log != null && log.isLoggable(Level.WARNING)) {
270 log.warning("... destroying streamSinkOutput(" + config.getBotId().getToken() + ").");
271 }
272 try {
273 if (streamSinkOutput != null) streamSinkOutput.interrupt();
274 } catch (Exception e) {
275 }
276 streamSinkOutput = null;
277
278 if (log != null && log.isLoggable(Level.WARNING)) {
279 log.warning("... destroying waitForEnd(" + config.getBotId().getToken() + ").");
280 }
281 if (!waitForEndThread) {
282 try {
283 if (this.waitForEndThread != null) this.waitForEndThread.interrupt();
284 } catch (Exception e) {
285 }
286 }
287 this.waitForEndThread = null;
288
289 if (log != null && log.isLoggable(Level.WARNING)) {
290 log.warning("... removing shutDownHook(" + config.getBotId().getToken() + ").");
291 }
292 if (shutDownHookThread != null) {
293 try {
294 Runtime.getRuntime().removeShutdownHook(shutDownHookThread);
295 } catch (Exception e) {
296 }
297 }
298 shutDownHookThread = null;
299
300 if (log != null && log.isLoggable(Level.WARNING)) {
301 log.warning("... setting running-flag(" + config.getBotId().getToken() + ") to FALSE.");
302 }
303 running.setFlag(false);
304
305 if (log != null && log.isLoggable(Level.WARNING)) {
306 log.warning("Shutdown(" + config.getBotId().getToken() + ") finished.");
307 }
308 }
309 }
310
311
312
313
314 public void stop() {
315 shutdown(false);
316 }
317
318
319
320
321
322 public Process getBotProcess() {
323 return botProcess;
324 }
325
326
327
328
329
330
331 public ImmutableFlag<Boolean> getRunning() {
332 return running.getImmutable();
333 }
334
335
336
337
338
339
340 public boolean isRunning() {
341 return running.getFlag();
342 }
343
344 }