1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package aigames.soccer.application;
21
22 import aigames.soccer.Move;
23
24 import org.dom4j.Document;
25 import org.dom4j.DocumentException;
26 import org.dom4j.DocumentHelper;
27
28 import snifos.common.UserId;
29
30 import snifos.server.Server;
31
32 import java.util.Calendar;
33 import java.util.Collections;
34 import java.util.Date;
35 import java.util.GregorianCalendar;
36 import java.util.HashMap;
37 import java.util.Iterator;
38 import java.util.Map;
39
40
41 /***
42 * This is a helper class used to send messages to users. It maintains
43 * confirmation mechanism and unique message ids.
44 * @version $Id: MessageSender.java,v 1.19 2004/05/08 21:55:26 mwerla Exp $
45 */
46 public class MessageSender {
47 private static Document ACCEPTED_TEMPLATE;
48 private static Document REFUSED_TEMPLATE;
49 private static Document PARAMS_TEMPLATE;
50 private static Document GAME_OVER_TEMPLATE;
51 private static final int MESSAGE_TIMEOUT = 30000;
52
53 static {
54 ACCEPTED_TEMPLATE = defineTemplate(
55 "<message type=\"confirmation\" id=\"0\">" + "<accepted/>" +
56 "</message>");
57
58 REFUSED_TEMPLATE = defineTemplate(
59 "<message type=\"confirmation\" id=\"0\">" + "<refused/>" +
60 "</message>");
61
62 PARAMS_TEMPLATE = defineTemplate("<message type=\"params\" id=\"0\">" +
63 "<map width=\"10\" height=\"8\" goal=\"2\"/>" +
64 "<attack side=\"-1\" begin=\"true\"/>" + "</message>");
65 GAME_OVER_TEMPLATE = defineTemplate(
66 "<message type=\"game-over\" id=\"0\">" +
67 "<name>Wymiatacz</name>" + "</message>");
68 }
69
70 private Server server;
71 private int counter = 0;
72 private Map unconfirmedMessagesByIdMap = Collections.synchronizedMap(new HashMap());
73 private Object counterSemaphore = new Object();
74
75 /***
76 * Constructo which sets server that will be used to send messages.
77 * @param server Server to set.
78 */
79 public MessageSender(Server server) {
80 this.server = server;
81 }
82
83 /***
84 * Returns message id counter.
85 * @return Message id counter.
86 */
87 public int getCounter() {
88 synchronized (counterSemaphore) {
89 return counter;
90 }
91 }
92
93 private static Document defineTemplate(String body) {
94 Document template = null;
95
96 try {
97 template = DocumentHelper.parseText(body);
98 } catch (DocumentException e) {
99
100 }
101
102 return template;
103 }
104
105 /***
106 * Sends game parameters to given user.
107 * @param userId User id.
108 * @param width Field width.
109 * @param height Field height.
110 * @param goal Goal height.
111 * @param side Attack side.
112 * @param begin True if user starts the game.
113 */
114 public void sendGameParameters(UserId userId, int width, int height,
115 int goal, int side, boolean begin) {
116 Document message = (Document) PARAMS_TEMPLATE.clone();
117
118 message.selectSingleNode("//message/map/@width").setText(String.valueOf(
119 width));
120 message.selectSingleNode("//message/map/@height").setText(String.valueOf(
121 height));
122 message.selectSingleNode("//message/map/@goal").setText(String.valueOf(
123 goal));
124 message.selectSingleNode("//message/attack/@side").setText(String.valueOf(
125 side));
126 message.selectSingleNode("//message/attack/@begin").setText(String.valueOf(
127 begin));
128
129 sendWithConfirmation(userId, message);
130 }
131
132 private synchronized int getNextMessageId() {
133 return counter++;
134 }
135
136 private void recordMessage(UserId userId, int messageId, Document message) {
137 Message sentMessage = new Message(userId, message);
138 sentMessage.setSendDate(new Date());
139
140 synchronized (unconfirmedMessagesByIdMap) {
141 unconfirmedMessagesByIdMap.put(new Integer(messageId), sentMessage);
142 }
143 }
144
145 /***
146 * Sends refused message to given user.
147 * @param message Message that is refused.
148 * @param reason Refuesd reason.
149 */
150 public void sendRefused(Message message, String reason) {
151 Document confirmation = (Document) REFUSED_TEMPLATE.clone();
152 confirmation.selectSingleNode("//message/refused").setText(reason);
153 sendConfirmation(message, confirmation);
154 }
155
156 private void sendConfirmation(Message message, Document confirmation) {
157 confirmation.selectSingleNode("//message/@id").setText(message.getMessageIdAsString());
158 server.sendMessage(message.getUserId(), confirmation);
159 }
160
161 /***
162 * Sends accepted message.
163 * @param message Message that is accepted.
164 */
165 public void sendAccepted(Message message) {
166 Document confirmation = (Document) ACCEPTED_TEMPLATE.clone();
167 sendConfirmation(message, confirmation);
168 }
169
170 /***
171 * Sends game over message.
172 * @param winner Name of the winner.
173 * @param userId User that should receive this message.
174 */
175 public void sendGameOver(String winner, UserId userId) {
176 Document gameOverMessage = (Document) GAME_OVER_TEMPLATE.clone();
177 gameOverMessage.selectSingleNode("//message/name").setText(winner);
178 sendWithConfirmation(userId, gameOverMessage);
179 }
180
181 /***
182 * Checks message confirmation timeouts, and resend messages if needed.
183 */
184 public void checkConfirmationTimeouts() {
185 Calendar calendar = new GregorianCalendar();
186
187 synchronized (unconfirmedMessagesByIdMap) {
188 for (Iterator iter = unconfirmedMessagesByIdMap.values().iterator();
189 iter.hasNext();) {
190 Message m = (Message) iter.next();
191 calendar.setTime(m.getSendDate());
192 calendar.add(Calendar.MILLISECOND, MESSAGE_TIMEOUT);
193
194 if (calendar.before(new Date())) {
195
196 server.sendMessage(m.getUserId(), m.getMessage());
197 m.setSendDate(new Date());
198 m.incResendCounter();
199 }
200 }
201 }
202 }
203
204 /***
205 * Confirms message.
206 * @param confirmationMessage Message confirmation.
207 */
208 public void setMessageConfirmation(Message confirmationMessage) {
209 synchronized (unconfirmedMessagesByIdMap) {
210 int id = confirmationMessage.getMessageId();
211 Integer intId = new Integer(id);
212
213 if (unconfirmedMessagesByIdMap.containsKey(intId)) {
214 unconfirmedMessagesByIdMap.remove(intId);
215 }
216 }
217 }
218
219 /***
220 * Forwards incoming message to other user.
221 * @param message Message that should be forwarded.
222 * @param userId User that shoud recieve the message.
223 */
224 public void forwardIncoming(Document message, UserId userId) {
225 Document messageToSend = (Document) message.clone();
226 sendWithConfirmation(userId, messageToSend);
227 }
228
229 /***
230 * Sends message and recods it to await for its confirmation.
231 * @param userId User that shoud recieve the message.
232 * @param messageToSend Message that should be sent.
233 */
234 public void sendWithConfirmation(UserId userId, Document messageToSend) {
235 int id = getNextMessageId();
236 messageToSend.selectSingleNode("//message/@id").setText(String.valueOf(
237 id));
238 server.sendMessage(userId, messageToSend);
239 recordMessage(userId, id, messageToSend);
240 }
241
242 /***
243 * Checks if there are unconfirmed messages.
244 * @return True if there are unconfirmed messages, otherwise false.
245 */
246 public boolean areUnconfirmedMessages() {
247 return unconfirmedMessagesByIdMap.size() > 0;
248 }
249
250 /***
251 * Clears all unconfirmed messages.
252 */
253 public void clearUnconfirmedMessages() {
254 unconfirmedMessagesByIdMap.clear();
255 }
256
257 /***
258 * Sends game over message as log message.
259 * @param winner Winner name.
260 * @param id User that shoud recieve the message.
261 */
262 public void sendLogGameOver(String winner, UserId id) {
263 LogMessageBuilder lmb = new LogMessageBuilder();
264 lmb.addWinner(winner);
265 sendWithConfirmation(id, lmb.getMessage());
266 }
267
268 /***
269 * Sends move message as log message.
270 * @param move Move to be sent.
271 * @param id User that shoud recieve the message.
272 * @param date Date at which move was made.
273 */
274 public void sendLogMove(Move move, UserId id, Date date) {
275 LogMessageBuilder lmb = new LogMessageBuilder();
276 lmb.addMove(move, date);
277 sendWithConfirmation(id, lmb.getMessage());
278 }
279 }