1 package cz.cuni.amis.pogamut.ut2004.teamcomm.mina.client;
2
3 import java.io.Serializable;
4 import java.net.InetSocketAddress;
5 import java.util.Collections;
6 import java.util.HashSet;
7 import java.util.Map;
8 import java.util.Set;
9 import java.util.Timer;
10 import java.util.TimerTask;
11 import java.util.logging.Logger;
12
13 import org.apache.mina.core.future.ConnectFuture;
14 import org.apache.mina.core.future.IoFutureListener;
15 import org.apache.mina.core.service.IoHandler;
16 import org.apache.mina.core.session.IdleStatus;
17 import org.apache.mina.core.session.IoSession;
18 import org.apache.mina.filter.codec.ProtocolCodecFilter;
19 import org.apache.mina.filter.codec.serialization.ObjectSerializationCodecFactory;
20 import org.apache.mina.transport.socket.nio.NioSocketConnector;
21
22 import cz.cuni.amis.pogamut.base.communication.translator.event.IWorldChangeEvent;
23 import cz.cuni.amis.pogamut.base.communication.worldview.IWorldView;
24 import cz.cuni.amis.pogamut.base.communication.worldview.event.IWorldEvent;
25 import cz.cuni.amis.pogamut.unreal.communication.messages.UnrealId;
26 import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.Player;
27 import cz.cuni.amis.pogamut.ut2004.teamcomm.bot.UT2004TCClient;
28 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.client.messages.TCRequestCreateChannel;
29 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.client.messages.TCRequestDestroyChannel;
30 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.client.messages.TCRequestGetStatus;
31 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.client.messages.TCRequestJoinChannel;
32 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.client.messages.TCRequestLeaveChannel;
33 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.client.messages.TCRequestRegister;
34 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.messages.TCInfoData;
35 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.messages.TCInfoMessage;
36 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.messages.TCMessage;
37 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.messages.TCRecipient;
38 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.messages.TCRequestMessage;
39 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.model.TCChannel;
40 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.model.TCTeam;
41 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.server.TCMinaServer;
42 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.server.messages.TCInfoBotJoined;
43 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.server.messages.TCInfoBotLeft;
44 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.server.messages.TCInfoRequestFailed;
45 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.server.messages.TCInfoRequestFailedException;
46 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.server.messages.TCInfoStatus;
47 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.server.messages.TCInfoTeamChannelBotJoined;
48 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.server.messages.TCInfoTeamChannelBotLeft;
49 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.server.messages.TCInfoTeamChannelCreated;
50 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.server.messages.TCInfoTeamChannelDestroyed;
51 import cz.cuni.amis.utils.ExceptionToString;
52 import cz.cuni.amis.utils.NullCheck;
53 import cz.cuni.amis.utils.flag.Flag;
54 import cz.cuni.amis.utils.flag.ImmutableFlag;
55 import cz.cuni.amis.utils.future.FutureWithListeners;
56 import cz.cuni.amis.utils.maps.HashMapMap;
57 import cz.cuni.amis.utils.token.IToken;
58
59 public class TCMinaClient implements IoHandler {
60
61 private static final int RETRY_REGISTER_PERIOD_SECS = 3;
62
63 private Object mutex = new Object();
64
65 private UT2004TCClient owner;
66
67 private IWorldView teamWorldView;
68
69 private UnrealId botId;
70
71 private int botTeam;
72
73 private InetSocketAddress address;
74
75 private Logger log;
76
77 private Flag<Boolean> connected = new Flag<Boolean>(false);
78
79 private Flag<Boolean> connecting = new Flag<Boolean>(false);
80
81 private IoFutureListener<ConnectFuture> connectionListener = new IoFutureListener<ConnectFuture>() {
82
83 @Override
84 public void operationComplete(ConnectFuture event) {
85 connected(event);
86 }
87 };
88
89 private Timer timer;
90
91 private NioSocketConnector ioConnector;
92
93 private ConnectFuture connectFuture;
94
95 private IoSession session;
96
97 private Set<UnrealId> allBots = new HashSet<UnrealId>();
98
99 private TCTeam team;
100
101 private int registerTries = 0;
102
103 public TCMinaClient(UT2004TCClient owner, InetSocketAddress connectToAddress, IWorldView teamWorldView, Logger log) {
104 this.owner = owner;
105 this.teamWorldView = teamWorldView;
106 this.botId = owner.getBotId();
107 this.botTeam = owner.getBotTeam();
108 this.address = connectToAddress;
109 NullCheck.check(this.address, "connectToAddress");
110 this.log = log;
111 NullCheck.check(this.log, "log");
112 }
113
114
115
116
117
118 public String getHost() {
119 return address.getHostName();
120 }
121
122 public int getPort() {
123 return address.getPort();
124 }
125
126
127
128
129
130 public IWorldView getWorldView() {
131 return teamWorldView;
132 }
133
134 public ImmutableFlag<Boolean> getConnected() {
135 return connected.getImmutable();
136 }
137
138 public ImmutableFlag<Boolean> getConnecting() {
139 return connecting.getImmutable();
140 }
141
142 public void connect() {
143 synchronized(mutex) {
144 if (connected.getFlag()) return;
145 if (connecting.getFlag()) return;
146 log.warning("Connecting to TC at " + getHost() + ":" + getPort() + " ...");
147 connecting.setFlag(true);
148 }
149
150 try {
151 ioConnector = new NioSocketConnector();
152
153 ioConnector.setHandler(this);
154
155 ioConnector.getFilterChain().addLast("codec", new ProtocolCodecFilter(new ObjectSerializationCodecFactory()));
156
157 connectFuture = ioConnector.connect(address);
158
159 connectFuture.addListener(connectionListener);
160 } catch (Exception e1) {
161 try {
162 connecting.setFlag(false);
163 } catch (Exception e2) {
164 }
165 }
166
167 }
168
169
170
171
172
173 public boolean isConnected(UnrealId botId) {
174 if (botId == null) return false;
175 if (!getConnected().getFlag()) return false;
176 return allBots.contains(botId);
177 }
178
179 public boolean isConnected(Player bot) {
180 if (bot == null) return false;
181 if (!getConnected().getFlag()) return false;
182 return allBots.contains(bot.getId());
183 }
184
185 public boolean isConnectedToMyTeam(UnrealId botId) {
186 if (botId == null) return false;
187 if (!getConnected().getFlag()) return false;
188 return team.getConnectedBots().contains(botId);
189 }
190
191 public boolean isConnectedToMyTeam(Player bot) {
192 if (bot == null) return false;
193 if (!getConnected().getFlag()) return false;
194 return team.getConnectedBots().contains(bot.getId());
195 }
196
197 public boolean isConnectedToChannel(UnrealId botId, int channelId) {
198 if (botId == null) return false;
199 if (!getConnected().getFlag()) return false;
200 TCChannel channel = team.getChannels().get(channelId);
201 if (channel == null) return false;
202 return channel.getConnectedBots().contains(botId);
203 }
204
205 public boolean isConnectedToChannel(Player bot, int channelId) {
206 if (bot == null) return false;
207 if (!getConnected().getFlag()) return false;
208 TCChannel channel = team.getChannels().get(channelId);
209 if (channel == null) return false;
210 return channel.getConnectedBots().contains(bot.getId());
211 }
212
213 public boolean isChannelExist(int channelId) {
214 if (!getConnected().getFlag()) return false;
215 TCChannel channel = team.getChannels().get(channelId);
216 if (channel == null) return false;
217 return true;
218 }
219
220
221
222
223
224 public Set<UnrealId> getConnectedAllBots() {
225 if (!getConnected().getFlag()) return new HashSet<UnrealId>();
226 return Collections.unmodifiableSet(allBots);
227 }
228
229
230
231
232
233 public Set<UnrealId> getConnectedTeamBots() {
234 if (!getConnected().getFlag()) return new HashSet<UnrealId>();
235 return Collections.unmodifiableSet(team.getConnectedBots());
236 }
237
238
239
240
241
242 public Set<UnrealId> getConnectedChannelBots(int channelId) {
243 if (!getConnected().getFlag()) return new HashSet<UnrealId>();
244 TCChannel channel = team.getChannels().get(channelId);
245 if (channel == null) return new HashSet<UnrealId>();
246 return Collections.unmodifiableSet(channel.getConnectedBots());
247 }
248
249
250
251
252
253
254
255
256 public TCTeam getTeam() {
257 synchronized(mutex) {
258 if (!getConnected().getFlag()) return null;
259 if (team == null) return null;
260 return team.clone();
261 }
262 }
263
264
265
266
267
268
269
270
271 public TCChannel getChannel(int channelId) {
272 synchronized(mutex) {
273 if (!getConnected().getFlag()) return null;
274 if (team == null) return null;
275 TCChannel channel = team.getChannels().get(channelId);
276 if (channel == null) return null;
277 return channel.clone();
278 }
279 }
280
281
282
283
284
285 public static class RequestFuture<T> extends FutureWithListeners<T> {
286
287 private TCRequestMessage message;
288
289 public RequestFuture(TCRequestMessage message) {
290 this.message = message;
291 }
292 }
293
294
295
296
297 private HashMapMap<IToken, Long, RequestFuture<?>> requestFutures = new HashMapMap<IToken, Long, RequestFuture<?>>();
298
299
300
301
302
303
304
305
306
307 public RequestFuture<TCInfoTeamChannelCreated> requestCreateChannel() {
308 TCRequestCreateChannel data = new TCRequestCreateChannel(owner.getSimTime());
309 TCRequestMessage message = new TCRequestMessage(botId, data);
310 RequestFuture<TCInfoTeamChannelCreated> future = new RequestFuture<TCInfoTeamChannelCreated>(message);
311 synchronized(mutex) {
312 if (!getConnected().getFlag()) return null;
313 requestFutures.put(data.getMessageType(), data.getRequestId(), future);
314 session.write(message);
315
316 }
317 return future;
318 }
319
320
321
322
323
324
325 public RequestFuture<TCInfoTeamChannelDestroyed> requestDestroyChannel(int channelId) {
326 TCRequestDestroyChannel data = new TCRequestDestroyChannel(owner.getSimTime());
327 data.setChannelId(channelId);
328 TCRequestMessage message = new TCRequestMessage(botId, data);
329 RequestFuture<TCInfoTeamChannelDestroyed> future = new RequestFuture<TCInfoTeamChannelDestroyed>(message);
330 synchronized(mutex) {
331 if (!getConnected().getFlag()) return null;
332 TCChannel channel = team.getChannels().get(channelId);
333 if (channel == null) return null;
334 if (!channel.getCreator().equals(botId)) return null;
335 requestFutures.put(data.getMessageType(), data.getRequestId(), future);
336 session.write(message);
337 }
338 return future;
339 }
340
341
342
343
344
345 public RequestFuture<TCInfoStatus> requestGetStatus() {
346 TCRequestGetStatus data = new TCRequestGetStatus(owner.getSimTime());
347 TCRequestMessage message = new TCRequestMessage(botId, data);
348 RequestFuture<TCInfoStatus> future = new RequestFuture<TCInfoStatus>(message);
349 synchronized(mutex) {
350 if (!getConnected().getFlag()) return null;
351 requestFutures.put(data.getMessageType(), data.getRequestId(), future);
352 session.write(message);
353
354 }
355 return future;
356 }
357
358
359
360
361
362
363 public RequestFuture<TCInfoTeamChannelBotJoined> requestJoinChannel(int channelId) {
364 TCRequestJoinChannel data = new TCRequestJoinChannel(owner.getSimTime());
365 data.setChannelId(channelId);
366 TCRequestMessage message = new TCRequestMessage(botId, data);
367 RequestFuture<TCInfoTeamChannelBotJoined> future = new RequestFuture<TCInfoTeamChannelBotJoined>(message);
368 synchronized(mutex) {
369 if (!getConnected().getFlag()) return null;
370 TCChannel channel = team.getChannels().get(channelId);
371 if (channel == null) return null;
372 requestFutures.put(data.getMessageType(), data.getRequestId(), future);
373 session.write(message);
374 }
375 return future;
376 }
377
378
379
380
381
382
383 public RequestFuture<TCInfoTeamChannelBotLeft> requestLeaveChannel(int channelId) {
384 TCRequestLeaveChannel data = new TCRequestLeaveChannel(owner.getSimTime());
385 data.setChannelId(channelId);
386 TCRequestMessage message = new TCRequestMessage(botId, data);
387 RequestFuture<TCInfoTeamChannelBotLeft> future = new RequestFuture<TCInfoTeamChannelBotLeft>(message);
388 synchronized(mutex) {
389 if (!getConnected().getFlag()) return null;
390 TCChannel channel = team.getChannels().get(channelId);
391 if (channel == null) return null;
392 requestFutures.put(data.getMessageType(), data.getRequestId(), future);
393 session.write(message);
394 }
395 return future;
396 }
397
398
399
400
401
402
403
404
405
406
407
408 public boolean sendToAll(IToken messageType, Serializable data) {
409 if (messageType == null) return false;
410
411 TCMessage message = new TCMessage(botId, TCRecipient.GLOBAL, messageType, data, owner.getSimTime());
412
413 synchronized(mutex) {
414 if (!connected.getFlag() || session == null) return false;
415 try {
416 session.write(message);
417 } catch (Exception e) {
418 log.warning(ExceptionToString.process("Failed to sendToAll: " + data, e));
419 return false;
420 }
421 return true;
422 }
423 }
424
425
426
427
428
429
430
431 public boolean sendToTeam(IToken messageType, Serializable data) {
432 if (messageType == null) return false;
433
434 TCMessage message = new TCMessage(botId, TCRecipient.TEAM, messageType, data, owner.getSimTime());
435
436 synchronized(mutex) {
437 if (!connected.getFlag() || session == null) return false;
438 try {
439 session.write(message);
440 } catch (Exception e) {
441 log.warning(ExceptionToString.process("Failed to sendToTeam: " + data, e));
442 return false;
443 }
444 return true;
445 }
446 }
447
448 public boolean sendToChannel(int channelId, IToken messageType, Serializable data) {
449 if (messageType == null) return false;
450
451 TCMessage message = new TCMessage(botId, TCRecipient.CHANNEL, messageType, data, owner.getSimTime());
452 message.setChannelId(channelId);
453
454 synchronized(mutex) {
455 if (!connected.getFlag() || session == null) return false;
456 try {
457 session.write(message);
458 } catch (Exception e) {
459 log.warning(ExceptionToString.process("Failed to sendToChannel(" + channelId + "): " + data, e));
460 return false;
461 }
462 return true;
463 }
464 }
465
466 public boolean sendPrivate(UnrealId targetBotId, IToken messageType, Serializable data) {
467 if (messageType == null) return false;
468
469 TCMessage message = new TCMessage(botId, TCRecipient.PRIVATE, messageType, data, owner.getSimTime());
470 message.setTargetId(targetBotId);
471
472 synchronized(mutex) {
473 if (!connected.getFlag() || session == null) return false;
474 try {
475 session.write(message);
476 } catch (Exception e) {
477 log.warning(ExceptionToString.process("Failed to sendPrivate(" + targetBotId.getStringId() + "): " + data, e));
478 return false;
479 }
480 return true;
481 }
482 }
483
484
485
486
487
488 protected void connected(ConnectFuture event) {
489 log.info("Connected to TC at " + getHost() + ":" + getPort());
490
491 this.session = event.getSession();
492
493 connectFuture.removeListener(connectionListener);
494 connectFuture = null;
495
496 log.info("Sending REGISTER request, expecting TCInfoStatus reply...");
497
498 registerTries = 1;
499 sendRegisterRequest();
500 }
501
502
503 private void sendRegisterRequest() {
504 TCRequestRegister request = new TCRequestRegister(owner.getSimTime());
505 TCRequestMessage message = new TCRequestMessage(botId, request);
506 synchronized(mutex) {
507 session.write(message);
508 }
509 }
510
511
512
513
514
515 @Override
516 public void exceptionCaught(IoSession session, Throwable exception) throws Exception {
517 log.warning(ExceptionToString.process("TCMinaClient Exception", exception));
518 stop();
519 }
520
521 @Override
522 public void messageReceived(IoSession session, Object message) throws Exception {
523 if (message == null) {
524 log.warning("Invalid message: " + String.valueOf(message));
525 return;
526 }
527 if (!(message instanceof TCMessage)) {
528 log.warning("Invalid message: " + String.valueOf(message));
529 return;
530 }
531
532 TCMessage tcMessage = (TCMessage)message;
533
534 if (tcMessage.getSource() == null) {
535 log.warning("TCMessage.getSource() is NULL, cannot process: " + String.valueOf(message));
536 return;
537 }
538 if (tcMessage.getMessageType() == null) {
539 log.warning("TCMessage.getMessageType() is NULL, cannot process: " + String.valueOf(message));
540 return;
541 }
542 if (tcMessage.getTarget() == null) {
543 log.warning("TCMessage.getTarget() is NULL, cannot process the message: " + String.valueOf(message));
544 return;
545 }
546
547 switch(tcMessage.getTarget()) {
548 case GLOBAL:
549 case TEAM:
550 case CHANNEL:
551 case PRIVATE:
552 Serializable data;
553 try {
554 data = tcMessage.getMessage();
555 } catch (Exception e) {
556 log.warning(ExceptionToString.process("Invalid request data, failed to deserialize TCMessage.getMessage(): " + String.valueOf(tcMessage), e));
557 return;
558 }
559 botMessage(tcMessage, data);
560 return;
561 case TC_INFO:
562 if (!(tcMessage instanceof TCInfoMessage)) {
563 log.warning("TCMessage recipient is " + tcMessage.getTarget() + ", but the message is not TCInfoMessage: " + String.valueOf(tcMessage));
564 return;
565 }
566 TCInfoData infoData = null;
567 try {
568 infoData = (TCInfoData)tcMessage.getMessage();
569 } catch (Exception e) {
570 log.warning("Invalid request data, failed to deserialize TCMessage.getMessage() as TCInfoMessageData: " + String.valueOf(tcMessage));
571 return;
572 }
573 infoMessage((TCInfoMessage)tcMessage, infoData);
574 return;
575 case TC_REQUEST:
576 log.warning("Received TC_REQUEST message, cannot process: " + String.valueOf(message));
577 return;
578 }
579 }
580
581 @Override
582 public void messageSent(IoSession session, Object message) throws Exception {
583 }
584
585 @Override
586 public void sessionClosed(IoSession session) throws Exception {
587 synchronized(mutex) {
588 if (timer != null) {
589 timer.cancel();
590 timer = null;
591 }
592
593 session = null;
594
595 log.warning("TC Server connection closed.");
596
597 connected.setFlag(false);
598 connecting.setFlag(false);
599 }
600 }
601
602 @Override
603 public void sessionCreated(IoSession session) throws Exception {
604 }
605
606 @Override
607 public void sessionIdle(IoSession session, IdleStatus status) throws Exception {
608 }
609
610 @Override
611 public void sessionOpened(IoSession session) throws Exception {
612 }
613
614
615
616
617
618 private void botMessage(TCMessage message, Serializable data) {
619 notify(message, data);
620 }
621
622 private void infoMessage(TCInfoMessage message, TCInfoData data) {
623 if (data.getMessageType() == TCInfoStatus.MESSAGE_TYPE) {
624 status(message, (TCInfoStatus)data);
625 } else
626 if (data.getMessageType() == TCInfoBotJoined.MESSAGE_TYPE) {
627 botJoined(message, (TCInfoBotJoined)data);
628 } else
629 if (data.getMessageType() == TCInfoBotLeft.MESSAGE_TYPE) {
630 botLeft(message, (TCInfoBotLeft)data);
631 } else
632 if (data.getMessageType() == TCInfoRequestFailed.MESSAGE_TYPE) {
633 requestFailed(message, (TCInfoRequestFailed)data);
634 } else
635 if (data.getMessageType() == TCInfoRequestFailed.MESSAGE_TYPE) {
636 status(message, (TCInfoStatus)data);
637 } else
638 if (data.getMessageType() == TCInfoTeamChannelBotJoined.MESSAGE_TYPE) {
639 channelBotJoined(message, (TCInfoTeamChannelBotJoined)data);
640 } else
641 if (data.getMessageType() == TCInfoTeamChannelBotLeft.MESSAGE_TYPE) {
642 channelBotLeft(message, (TCInfoTeamChannelBotLeft)data);
643 } else
644 if (data.getMessageType() == TCInfoTeamChannelCreated.MESSAGE_TYPE) {
645 channelCreated(message, (TCInfoTeamChannelCreated)data);
646 } else
647 if (data.getMessageType() == TCInfoTeamChannelDestroyed.MESSAGE_TYPE) {
648 channelDestroyed(message, (TCInfoTeamChannelDestroyed)data);
649 } else {
650 log.warning("Unhandled INFO message type: " + data.getMessageType().getToken());
651 }
652 }
653
654
655
656
657
658 private void botJoined(TCInfoMessage message, TCInfoBotJoined data) {
659 if (data.getBotId() == null) {
660 log.warning("TCInfoBotJoined.getBotId() is NULL!");
661 return;
662 }
663
664 log.info("Bot " + data.getBotId().getStringId() + " has joined TC.");
665
666 synchronized(mutex) {
667 allBots.add(data.getBotId());
668
669 if (team == null) return;
670
671 if (data.getTeam() != team.getTeam()) {
672 return;
673 }
674
675 team.getConnectedBots().add(data.getBotId());
676 }
677
678 notify(message, data);
679 }
680
681 private void botLeft(TCInfoMessage message, TCInfoBotLeft data) {
682 if (data.getBotId() == null) {
683 log.warning("TCInfoBotLeft.getBotId() is NULL!");
684 return;
685 }
686
687 log.warning("Bot " + data.getBotId().getStringId() + " has left TC.");
688
689 synchronized(mutex) {
690 allBots.add(data.getBotId());
691
692 if (team == null) return;
693
694 if (data.getTeam() != team.getTeam()) {
695 return;
696 }
697
698 team.getConnectedBots().remove(data.getBotId());
699 for (TCChannel channel : team.getChannels().values()) {
700 channel.getConnectedBots().remove(data.getBotId());
701 }
702 }
703
704 notify(message, data);
705 }
706
707 private void requestFailed(TCInfoMessage message, TCInfoRequestFailed data) {
708 if (connecting.getFlag()) {
709 log.warning("Failed to register, will retry in " + RETRY_REGISTER_PERIOD_SECS + " seconds...");
710 if (timer == null) timer = new Timer();
711 timer.schedule(new TimerTask() {
712 @Override
713 public void run() {
714 ++registerTries;
715 log.warning("Trying to register again (" + registerTries + ")...");
716 sendRegisterRequest();
717 }
718 }, RETRY_REGISTER_PERIOD_SECS * 1000);
719 return;
720 }
721
722
723
724
725 failRequestFuture(null, data);
726
727 notify(message, data);
728 }
729
730 private void status(TCInfoMessage message, TCInfoStatus data) {
731 log.info("Received TCInfoStatus message...");
732
733 synchronized(mutex) {
734 allBots = new HashSet<UnrealId>(data.getAllBots());
735 team = data.getTeam();
736
737 if (connecting.getFlag()) {
738 log.info("Connected to TC Server at " + address.getHostName() + ":" + address.getPort());
739
740 timer = null;
741
742 connected.setFlag(true);
743 connecting.setFlag(false);
744 } else {
745 requestFinishedUnsync(TCRequestGetStatus.MESSAGE_TYPE, data.getRequestId(), data);
746 }
747 }
748
749 notify(message, data);
750 }
751
752 private void channelBotJoined(TCInfoMessage message, TCInfoTeamChannelBotJoined data) {
753 if (data.getBotId() == null) {
754 log.warning("TCInfoTeamChannelBotJoined.getBotId() is NULL!");
755 }
756 synchronized(mutex) {
757 TCChannel channel = team.getChannels().get(data.getChannelId());
758 if (channel == null) {
759 log.warning("Bot " + data.getBotId().getStringId() + " has joined unknown channel " + data.getChannelId() + "! Requesting STATUS...");
760 requestGetStatus();
761 return;
762 }
763 channel.getConnectedBots().add(data.getBotId());
764
765 requestFinishedUnsync(TCRequestJoinChannel.MESSAGE_TYPE, data.getRequestId(), data);
766 }
767 notify(message, data);
768 }
769
770 private void channelBotLeft(TCInfoMessage message, TCInfoTeamChannelBotLeft data) {
771 if (data.getBotId() == null) {
772 log.warning("TCInfoTeamChannelBotLeft.getBotId() is NULL!");
773 }
774 synchronized(mutex) {
775 TCChannel channel = team.getChannels().get(data.getChannelId());
776 if (channel == null) {
777 log.warning("Bot " + data.getBotId().getStringId() + " has left unknown channel " + data.getChannelId() + "! Requesting STATUS...");
778 requestGetStatus();
779 return;
780 }
781 channel.getConnectedBots().remove(data.getBotId());
782
783 requestFinishedUnsync(TCRequestLeaveChannel.MESSAGE_TYPE, data.getRequestId(), data);
784 }
785 notify(message, data);
786 }
787
788 private void channelCreated(TCInfoMessage message, TCInfoTeamChannelCreated data) {
789 if (team == null) return;
790 if (data.getChannel() == null) {
791 log.warning("TCInfoTeamChannelCreated.getChannel() is NULL!");
792 return;
793 }
794 synchronized(mutex) {
795 team.getChannels().put(data.getChannel().getChannelId(), data.getChannel().clone());
796 requestFinishedUnsync(TCRequestCreateChannel.MESSAGE_TYPE, data.getRequestId(), data);
797 }
798 notify(message, data);
799 }
800
801 private void channelDestroyed(TCInfoMessage message, TCInfoTeamChannelDestroyed data) {
802 synchronized(mutex) {
803 if (team == null) return;
804 team.getChannels().remove(data.getChannelId());
805 requestFinishedUnsync(TCRequestDestroyChannel.MESSAGE_TYPE, data.getRequestId(), data);
806 }
807 notify(message, data);
808 }
809
810
811
812
813
814 @SuppressWarnings("rawtypes")
815 private void requestFinishedUnsync(IToken requestMessageType, long requestId, Object result) {
816 Map<Long, RequestFuture<?>> futures = requestFutures.get(requestMessageType);
817 if (futures == null) return;
818 RequestFuture requestFuture = futures.remove(requestId);
819 if (requestFuture == null) return;
820 requestFuture.setResult(result);
821 }
822
823
824
825
826
827 private void failRequestFuture(IToken requestMessageType, TCInfoRequestFailed data) {
828 synchronized(mutex) {
829 if (requestMessageType == null) {
830 for (IToken messageType : requestFutures.keySet()) {
831 Map<Long, RequestFuture<?>> futures = requestFutures.get(messageType);
832 RequestFuture<?> requestFuture = futures.remove(data.getRequestId());
833 if (requestFuture != null) {
834 requestFuture.computationException(new TCInfoRequestFailedException(requestFuture.message, data, log, this));
835 }
836 }
837 } else {
838 Map<Long, RequestFuture<?>> futures = requestFutures.get(requestMessageType);
839 if (futures == null) return;
840 RequestFuture<?> requestFuture = futures.remove(data.getRequestId());
841 if (requestFuture == null) return;
842 requestFuture.computationException(new TCInfoRequestFailedException(requestFuture.message, data, log, this));
843 }
844 }
845 }
846
847 private void notify(TCMessage message, Serializable data) {
848 if (data instanceof IWorldChangeEvent && data instanceof IWorldEvent) {
849 teamWorldView.notify((IWorldChangeEvent)data);
850 }
851 if (message instanceof IWorldChangeEvent && message instanceof IWorldEvent) {
852 teamWorldView.notify(message);
853 }
854 }
855
856
857
858
859
860 public void stop() {
861 synchronized(mutex) {
862 log.info("Stopping TCMinaClient!");
863
864 allBots.clear();
865 team = null;
866
867 if (timer != null) {
868 timer.cancel();
869 timer = null;
870 }
871
872 try {
873 if (session != null) {
874 session.close(true);
875 }
876 } catch (Exception e) {
877 }
878 session = null;
879
880 try {
881 connected.setFlag(false);
882 } catch (Exception e) {
883 }
884 try {
885 connecting.setFlag(false);
886 } catch (Exception e) {
887 }
888
889 for (IToken messageType : requestFutures.keySet()) {
890 Map<Long, RequestFuture<?>> futures = requestFutures.get(messageType);
891 for (RequestFuture<?> future : futures.values()) {
892 future.cancel(true);
893 }
894 }
895 requestFutures.clear();
896
897 log.info("TCMinaClient stopped!");
898 }
899 }
900
901 }