View Javadoc

1   /*
2    * AI Soccer Project - network gaming environment for AI warriors.
3    * Copyright (C) 2001-2004  Marcin Werla, Pawel Widera
4    *
5    * This program is free software; you can redistribute it and/or
6    * modify it under the terms of the GNU General Public License
7    * as published by the Free Software Foundation; either version 2
8    * of the License, or (at your option) any later version.
9    *
10   * This program is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   * GNU General Public License for more details.
14   *
15   * You should have received a copy of the GNU General Public License
16   * along with this program; if not, you can find it here:
17   * http://www.gnu.org/licenses/gpl.html
18   */
19  
20  package aigames.soccer.observer;
21  
22  import org.apache.log4j.Logger;
23  
24  import org.dom4j.Document;
25  import org.dom4j.DocumentException;
26  import org.dom4j.DocumentHelper;
27  import org.dom4j.Element;
28  
29  import org.dom4j.io.SAXReader;
30  
31  import java.io.BufferedReader;
32  import java.io.ByteArrayInputStream;
33  import java.io.IOException;
34  import java.io.InputStream;
35  import java.io.InputStreamReader;
36  import java.io.PrintWriter;
37  
38  import java.net.Socket;
39  import java.net.UnknownHostException;
40  
41  
42  /***
43   * The observer communication class.
44   * @version $Id: Communication.java,v 1.7 2004/05/08 21:55:29 mwerla Exp $
45   */
46  public class Communication implements Runnable {
47      private static Logger logger = Logger.getLogger(Communication.class);
48  
49      /* end of transmission marker */
50      private static String inlineEOT = "\004";
51  
52      /* end of message line string */
53      private static String EOT = "\n" + inlineEOT + "\n";
54      private static int ACCEPTED = 101;
55      private static int NOT_ACCEPTED = 202;
56      private static int REFUSED = 303;
57      private Socket socket;
58      private Document doc;
59      private PrintWriter out = null;
60      private BufferedReader in = null;
61      private Observer observer;
62      private boolean active;
63  
64      /***
65       * Connect to a server.
66       * @param ip
67       * @param port
68       * @throws Exception Thrown when connection fails.
69       */
70      public void connect(String ip, int port) throws Exception {
71          try {
72              socket = new Socket(ip, port);
73              out = new PrintWriter(socket.getOutputStream(), false);
74              in = new BufferedReader(new InputStreamReader(
75                          socket.getInputStream()));
76          } catch (UnknownHostException e) {
77              logger.error("Unknown host: " + ip);
78              throw new Exception();
79          } catch (Exception e) {
80              logger.error(ip + ": " + e.getMessage());
81              throw new Exception();
82          }
83  
84          active = true;
85      }
86  
87      /***
88       * Disconnect from a server.
89       * @throws Exception Thrown when disconnection fails.
90       */
91      public void disconnect() throws Exception {
92          try {
93              out.close();
94              in.close();
95              socket.close();
96          } catch (IOException e) {
97              logger.error("Stream closing error: " + e.getMessage());
98              throw new Exception();
99          }
100     }
101 
102     /***
103      * Introduce the observer to a server.
104      * @param name observer name
105      * @throws Exception Thrown when message acceptation read fails.
106      */
107     public void sayHello(String name) throws Exception {
108         Document hello = DocumentHelper.createDocument();
109         Element message = hello.addElement("message");
110         message.addAttribute("type", "hello");
111         message.addAttribute("id", "1");
112         message.addElement("mod-type").setText("observer");
113         message.addElement("name").setText(name);
114 
115         int answer;
116         int counter = 0;
117 
118         do {
119             ++counter;
120             sendMessage(hello);
121             answer = isMessageAccepted("1");
122         } while ((NOT_ACCEPTED == answer) && (counter < 3));
123     }
124 
125     /***
126      * Read and parse a message from a socket.
127      * @param confirm if true the confirmation will be send
128      * @return dom4j Document
129      * @throws Exception Thrown on read or parse error.
130      */
131     public Document readMessage(boolean confirm) throws Exception {
132         StringBuffer buffer = null;
133         String message = null;
134 
135         try {
136             buffer = new StringBuffer();
137 
138             while (false == in.ready()) {
139                 Thread.sleep(100);
140             }
141 
142             while (null != (message = in.readLine())) {
143                 if (message.equals(inlineEOT)) {
144                     break;
145                 }
146 
147                 buffer.append(message);
148             }
149         } catch (IOException e) {
150             logger.error("I/O error: " + e.getMessage());
151             throw new Exception();
152         }
153 
154         String xml = buffer.toString();
155 
156         if (null != xml) {
157             InputStream stream = new ByteArrayInputStream(xml.getBytes());
158             SAXReader reader = new SAXReader();
159 
160             try {
161                 doc = reader.read(stream);
162             } catch (DocumentException e) {
163                 logger.error("Parsing error: " + e.getMessage());
164                 throw new Exception();
165             }
166 
167             if (confirm) {
168                 sendConfirmation();
169             }
170         } else {
171             doc = null;
172         }
173 
174         return doc;
175     }
176 
177     /***
178      * Read a list of games.
179      * @return list
180      */
181     public String[] getGamesList() {
182         //@TODO implement this fully
183         String[] list = new String[3];
184         list[0] = "Red vs. Blue";
185         list[1] = "PathExtractor vs. MegaMózg";
186         list[2] = "Runner vs. Runner";
187 
188         return list;
189     }
190 
191     /***
192      * Chosing a game on the server to observe.
193      * @param name of the game
194      */
195     public void chooseGame(String name) {
196         //@TODO implement
197     }
198 
199     /***
200      * Sending message as xml.
201      * @param message dom4j Document
202      */
203     private void sendMessage(Document message) {
204         String xml = message.asXML() + EOT;
205         out.print(xml);
206         out.flush();
207     }
208 
209     /***
210      * Confirming that the message was received.
211      */
212     private void sendConfirmation() {
213         String id = doc.valueOf("//message/@id");
214         Document confirm = DocumentHelper.createDocument();
215 
216         Element message = confirm.addElement("message");
217         message.addAttribute("type", "confirmation");
218         message.addAttribute("id", id);
219         message.addElement("accepted");
220         sendMessage(confirm);
221     }
222 
223     /***
224      * Check message confirmation from a server.
225      * @param id message id
226      * @return state of confirmation
227      * @throws Exception Thrown on read error.
228      */
229     private int isMessageAccepted(String id) throws Exception {
230         readMessage(false);
231 
232         String type = doc.valueOf("//message/@type");
233 
234         if (!type.equals("confirmation")) {
235             return NOT_ACCEPTED;
236         }
237 
238         String confirmedId = doc.valueOf("//message/@id");
239 
240         if (!id.equals(confirmedId)) {
241             return NOT_ACCEPTED;
242         }
243 
244         String reason = doc.valueOf("//message/refused");
245 
246         if (!reason.equals("")) {
247             logger.error("Message refused with: " + reason);
248 
249             return REFUSED;
250         }
251 
252         return ACCEPTED;
253     }
254 
255     /***
256      * Setter for observer object.
257      * @param observer
258      */
259     public void setObserver(Observer observer) {
260         this.observer = observer;
261     }
262 
263     /***
264      * Stops the reading loop.
265      */
266     public void shutdown() {
267         active = false;
268     }
269 
270     /***
271      * @see java.lang.Runnable#run()
272      */
273     public void run() {
274         while (active) {
275             try {
276                 readMessage(true);
277             } catch (Exception e) {
278                 try {
279                     disconnect();
280 
281                     return;
282                 } catch (Exception ee) {
283                     return;
284                 }
285             }
286 
287             if (null != doc) {
288                 observer.processMessage(doc);
289             }
290         }
291 
292         return;
293     }
294 }