1 package cz.cuni.amis.pogamut.ut2004.utils;
2
3 import java.io.BufferedReader;
4 import java.io.File;
5 import java.io.IOException;
6 import java.io.InputStream;
7 import java.io.InputStreamReader;
8 import java.util.Timer;
9 import java.util.TimerTask;
10 import java.util.concurrent.CountDownLatch;
11 import java.util.concurrent.TimeUnit;
12 import java.util.logging.Level;
13 import java.util.logging.Logger;
14 import java.util.regex.Matcher;
15
16 import cz.cuni.amis.pogamut.base.agent.impl.AgentId;
17 import cz.cuni.amis.pogamut.base.communication.connection.impl.socket.SocketConnectionAddress;
18 import cz.cuni.amis.pogamut.base.utils.Pogamut;
19 import cz.cuni.amis.pogamut.base.utils.logging.LogCategory;
20 import cz.cuni.amis.pogamut.base.utils.logging.LogPublisher;
21 import cz.cuni.amis.pogamut.ut2004.factory.direct.remoteagent.UT2004ServerFactory;
22 import cz.cuni.amis.pogamut.ut2004.server.IUT2004Server;
23 import cz.cuni.amis.pogamut.ut2004.server.exception.UCCStartException;
24 import cz.cuni.amis.utils.exception.PogamutException;
25 import cz.cuni.amis.utils.flag.Flag;
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42 public class UCCWrapper {
43
44
45 protected LogCategory uccLog;
46 protected static int fileCounter = 0;
47 Process uccProcess = null;
48
49 protected int gbPort = -1;
50
51 protected int controlPort = -1;
52
53 protected int observerPort = -1;
54 protected IUT2004Server utServer = null;
55
56 protected static final int basePort = 39782;
57 protected static Integer nextUccWrapperUID = 0;
58
59 protected int uccWrapperUID = 0;
60 protected UCCWrapperConf conf = null;
61
62
63
64
65
66 protected Flag<Boolean> gameEnding = new Flag<Boolean>(false);
67
68
69
70
71
72
73
74
75 public Logger getLogger() {
76 return uccLog;
77 }
78
79 public UCCWrapperConf getConfiguration() {
80 return conf;
81 }
82
83
84
85
86 @SuppressWarnings("unchecked")
87 public IUT2004Server getUTServer() {
88 stopCheck();
89 if (utServer == null) {
90 UT2004ServerFactory factory = new UT2004ServerFactory();
91 UT2004ServerRunner serverRunner = new UT2004ServerRunner(factory, "NBUTServer", "localhost", controlPort);
92 utServer = serverRunner.startAgent();
93 }
94 return utServer;
95 }
96
97 protected String getUnrealHome() {
98 if (conf.getUnrealHome() == null) {
99 return Pogamut.getPlatform().getProperty(PogamutUT2004Property.POGAMUT_UNREAL_HOME.getKey());
100 } else {
101 return conf.getUnrealHome();
102 }
103 }
104
105 public UCCWrapper(UCCWrapperConf configuration) throws UCCStartException {
106 uccLog = new LogCategory("Wrapper");
107 uccLog.addHandler(new LogPublisher.ConsolePublisher(new AgentId("UCC")));
108 if (configuration.log != null) {
109 uccLog.setParent(configuration.log);
110 }
111 this.conf = configuration;
112 uccWrapperUID = nextUccWrapperUID++;
113 initUCCWrapper();
114 Runtime.getRuntime().addShutdownHook(shutDownHook);
115 }
116
117
118
119 Thread shutDownHook = new Thread("UCC wrapper finalizer") {
120
121 @Override
122 public void run() {
123 if (uccProcess != null) uccProcess.destroy();
124 }
125 };
126
127
128
129
130 protected class StreamSink extends Thread {
131
132 protected InputStream os = null;
133
134 public StreamSink(InputStream os) {
135 setName("UCC Stream handler");
136 this.os = os;
137 }
138
139 protected void handleInput(String str) {
140 if (uccLog.isLoggable(Level.INFO)) uccLog.info("ID" + uccWrapperUID + " " + str);
141 }
142
143 @Override
144 public void run() {
145 BufferedReader stdInput = new BufferedReader(new InputStreamReader(os));
146
147 String s = null;
148 try {
149 while ((s = stdInput.readLine()) != null) {
150 handleInput(s);
151 }
152 os.close();
153 } catch (IOException ex) {
154
155
156
157 }
158 }
159 }
160
161
162
163
164 public class ScannerSink extends StreamSink {
165
166 public long startingTimeout = 2 * 60 * 1000;
167
168 public UCCStartException exception = null;
169
170 public ScannerSink(InputStream is) {
171 super(is);
172 timer.schedule(task = new TimerTask() {
173
174 @Override
175 public void run() {
176 exception = new UCCStartException("Starting timed out. Ports weren't bound in the required time (" + startingTimeout + " ms).", this);
177 timer.cancel();
178 portsBindedLatch.countDown();
179 }
180 }, startingTimeout);
181 }
182 public CountDownLatch portsBindedLatch = new CountDownLatch(1);
183 public int controlPort = -1;
184 public int botsPort = -1;
185
186
187
188
189
190 Timer timer = new Timer("UCC start timeout");
191 TimerTask task = null;
192 Matcher matcher;
193
194 @Override
195 protected void handleInput(String str) {
196 super.handleInput(str);
197
198 if (portsBindedLatch.getCount() != 0) {
199
200 if (conf.getPatterns().getObserverPortPattern() != null) {
201 matcher = conf.getPatterns().getObserverPortPattern().matcher(str);
202 if (matcher.find()) {
203 observerPort = Integer.parseInt(matcher.group(1));
204 }
205 }
206 if (conf.getPatterns().getControlPortPattern() != null) {
207 matcher = conf.getPatterns().getControlPortPattern().matcher(str);
208 if (matcher.find()) {
209 controlPort = Integer.parseInt(matcher.group(1));
210 }
211 }
212 if (conf.getPatterns().getBotPortPattern() != null) {
213 matcher = conf.getPatterns().getBotPortPattern().matcher(str);
214 if (matcher.find()) {
215 botsPort = Integer.parseInt(matcher.group(1));
216 raiseLatch();
217 }
218 }
219 if (conf.getPatterns().getCommandletNotFoundPattern() != null) {
220 matcher = conf.getPatterns().getCommandletNotFoundPattern().matcher(str);
221 if (matcher.find()) {
222 exception = new UCCStartException("UCC failed to start due to: Commandlet server not found.", this);
223 raiseLatch();
224 }
225 }
226 if (conf.getPatterns().getMapNotFoundPattern() != null) {
227 matcher = conf.getPatterns().getMapNotFoundPattern().matcher(str);
228 if (matcher.find()) {
229 exception = new UCCStartException("UCC failed to start due to: Map not found.", this);
230 raiseLatch();
231 }
232 }
233
234 if (conf.getPatterns().getExitingErrorPattern() != null) {
235 matcher = conf.getPatterns().getExitingErrorPattern().matcher(str);
236 if (matcher.find()) {
237 exception = new UCCStartException("UCC failed to start to due to some error.", this);
238 raiseLatch();
239 }
240 }
241
242 if (conf.getPatterns().getMatchStartedPattern() != null) {
243 matcher = conf.getPatterns().getMatchStartedPattern().matcher(str);
244 if (matcher.find()) {
245
246 raiseLatch();
247 }
248 } else {
249 exception = new UCCStartException("conf.getPatterns().getMatchStartedPattern() is NULL, there is no way how to recognize successful UCC startup.", this);
250 raiseLatch();
251 }
252 } else {
253 if (conf.getPatterns().getGameEndingPattern() != null) {
254 matcher = conf.getPatterns().getGameEndingPattern().matcher(str);
255 if (matcher.find()) {
256 gameEnding.setFlag(true);
257 }
258 }
259 }
260
261 }
262
263 protected void raiseLatch() {
264 timer.cancel();
265 task.cancel();
266 portsBindedLatch.countDown();
267 }
268 }
269 public static long stamp = System.currentTimeMillis();
270
271 protected void initUCCWrapper() throws UCCStartException {
272 boolean exception = false;
273 try {
274
275 String id = System.currentTimeMillis() + "a" + fileCounter++;
276 String fileWithPorts = "GBports" + id;
277 String uccHomePath = getUnrealHome();
278 String systemDirPath = uccHomePath + File.separator + "System" + File.separator;
279
280
281 String uccFile = "ucc.exe";
282
283
284 String options = "";
285 if (!System.getProperty("os.name").contains("Windows")) {
286 options = " -nohomedir";
287 uccFile = "ucc";
288 if (System.getProperty("os.name").toLowerCase().contains("linux")) {
289 uccFile = "ucc-bin";
290 if (System.getProperty("os.arch").toLowerCase().contains("amd64")) {
291 Logger.getLogger("UCCWrapper").info("64bit arch detected (os.arch property contains keyword amd64). Using 64bit binarry.");
292 uccFile += "-linux-amd64";
293 }
294 }
295 }
296
297 String execStr = systemDirPath + uccFile;
298 String portsSetting = conf.startOnUnusedPort ? "?PortsLog=" + fileWithPorts + "?bRandomPorts=true" : "";
299 String playerPortSetting = conf.playerPort != -1 ? "-port=" + conf.playerPort : "";
300
301 String parameter = conf.mapName
302 + "?game=" + conf.gameBotsPack + "." + conf.gameType
303 + portsSetting + conf.options + options;
304
305 ProcessBuilder procBuilder = new ProcessBuilder(execStr, "server", parameter, playerPortSetting);
306 procBuilder.directory(new File(systemDirPath));
307
308 uccProcess = procBuilder.start();
309 ScannerSink scanner = new ScannerSink(uccProcess.getInputStream());
310 scanner.start();
311 new StreamSink(uccProcess.getErrorStream()).start();
312
313 scanner.portsBindedLatch.await(3, TimeUnit.MINUTES);
314 if (scanner.exception != null) {
315
316 try {
317 uccProcess.destroy();
318 } catch (Exception e) {
319 }
320 uccProcess = null;
321 throw scanner.exception;
322 }
323 if (scanner.portsBindedLatch.getCount() > 0) {
324 scanner.interrupt();
325 try {
326 uccProcess.destroy();
327 } catch (Exception e) {
328 }
329 uccProcess = null;
330 throw new UCCStartException("UCC did not start in 3 minutes, timeout.", this);
331 }
332
333 controlPort = scanner.controlPort;
334 gbPort = scanner.botsPort;
335 } catch (InterruptedException ex1) {
336 exception = true;
337 throw new UCCStartException("Interrupted.", ex1);
338 } catch (IOException ex2) {
339 exception = true;
340 throw new UCCStartException("IO Exception.", ex2);
341 } catch (UCCStartException ex3) {
342 exception = true;
343 throw ex3;
344 } catch (Exception ex3) {
345 exception = true;
346 throw new UCCStartException("Exception.", ex3);
347 } finally {
348 if (exception) {
349 try {
350 stop();
351 } catch (Exception e){
352 }
353 }
354 }
355 }
356
357
358
359
360
361 public Process getProcess() {
362 return uccProcess;
363 }
364
365 protected boolean stopped = false;
366
367
368
369
370 public synchronized void stop() {
371 stopped = true;
372 if (uccProcess != null) {
373 uccProcess.destroy();
374 Runtime.getRuntime().removeShutdownHook(shutDownHook);
375 uccProcess = null;
376 try {
377 Thread.sleep(1000);
378
379 } catch (InterruptedException e) {
380
381 }
382 }
383 }
384
385
386
387
388 public int getBotPort() {
389 stopCheck();
390 return gbPort;
391 }
392
393
394
395
396 public int getObserverPort() {
397 stopCheck();
398 return observerPort;
399 }
400
401
402
403
404 public int getControlPort() {
405 stopCheck();
406 return controlPort;
407 }
408
409 protected void stopCheck() {
410 if (stopped) {
411 throw new PogamutException("UCC already stopped.", this);
412 }
413 }
414
415 public String getHost() {
416 return "localhost";
417 }
418
419 public SocketConnectionAddress getBotAddress() {
420 if (getBotPort() <= 0) {
421 throw new RuntimeException("Bot port is unavailable, wrong bot-port matching pattern?");
422 }
423 return new SocketConnectionAddress(getHost(), getBotPort());
424 }
425
426 public SocketConnectionAddress getServerAddress() {
427 if (getControlPort() <= 0) {
428 throw new RuntimeException("Control port is unavailable, wrong control-port matching pattern?");
429 }
430 return new SocketConnectionAddress(getHost(), getControlPort());
431 }
432
433 public SocketConnectionAddress getObserverAddress() {
434 if (getObserverPort() <= 0) {
435 throw new RuntimeException("Observer port is unavailable, wrong observer-port matching pattern?");
436 }
437 return new SocketConnectionAddress(getHost(), getObserverPort());
438 }
439
440
441
442
443
444 public Flag<Boolean> getGameEnding() {
445 return gameEnding;
446 }
447
448 }