View Javadoc

1   /*
2   @See License.txt@
3    */
4   
5   package spellcast.server;
6   
7   import java.io.ByteArrayOutputStream;
8   import java.io.IOException;
9   import java.io.InterruptedIOException;
10  import java.io.ObjectOutputStream;
11  import java.net.DatagramPacket;
12  import java.net.DatagramSocket;
13  import java.net.InetAddress;
14  
15  import org.apache.log4j.Logger;
16  import spellcast.net.NetConstants;
17  import spellcast.net.ServerStatus;
18  
19  /***
20   * The ServerStatusRequestHandler listens for ServerStatusRequest messages and
21   * handles them.  The class creates the datagram socket that is used 
22   * to receive the broadcast messages for ServerStatusRequests and creates
23   * the ServerStatusResponse message.  A new thread is started to handle the
24   * message processing for ServerStatusRequestHandler when <code>start</code> is called.
25   * Call <code>stop</code> to stop the thread.
26   *
27   * @author Barrie Treloar
28   */
29  public class ServerStatusRequestHandler implements NetConstants, Runnable {
30      /***
31       * The socket timeout is set to 10 seconds.
32       */
33      private static final int SOCKET_TIMEOUT = 10 * 1000;
34  
35      /***
36       * The datagram packet size is 1024 bytes.  Any packet exceeding this length
37       * will be truncated.
38       */
39      private static final int PACKET_SIZE = 1024;
40  
41      private static final Logger logger = Logger.getLogger("server.status");
42      private static final Logger logger_net = Logger.getLogger("server.status.net");
43  
44      private Thread serverStatusThread;
45      private DatagramSocket serverStatusSocket;
46      private boolean isRunning;
47  
48      public ServerStatusRequestHandler() {
49      }
50  
51      /***
52       * Send back the ServerStatusResponse.
53       */
54      private void sendResponse(DatagramPacket message) {
55          try {
56              ServerStatus ss = Server.getServer().getServerStatus();
57  
58              ByteArrayOutputStream baos = new ByteArrayOutputStream();
59              ObjectOutputStream oos = new ObjectOutputStream(baos);
60              oos.writeObject(ss);
61  
62              byte[] bytes = baos.toByteArray();
63              logger_net.debug("ServerStatusResponse.size = " + bytes.length);
64              logger_net.debug(
65                  "Sending to "
66                      + message.getAddress().getHostAddress()
67                      + ":"
68                      + message.getPort());
69              DatagramPacket response =
70                  new DatagramPacket(
71                      bytes,
72                      bytes.length,
73                      message.getAddress(),
74                      message.getPort());
75              serverStatusSocket.send(response);
76          }
77          catch (Exception ex) {
78              logger.error("Sending of server status response failed.", ex);
79          }
80      }
81  
82      /***
83       * This thread will start running when <code>start</code> is called and 
84       * continue running until the <code>stop</code> method is called.  
85       * This method loops while checking for a ServerStatusRequest message to be
86       * received.  When it is received it queries the current game for its status
87       * returns the status in a ServerStatusResponse message.
88       */
89      public void run() {
90          DatagramPacket message = new DatagramPacket(new byte[PACKET_SIZE], PACKET_SIZE);
91  
92          while (isRunning) {
93              try {
94                  serverStatusSocket.receive(message);
95                  sendResponse(message);
96              }
97              catch (InterruptedIOException ix) {
98                  // Ignore interrupted exceptions
99              }
100             catch (IOException ex) {
101                 logger.error("Failed on serverStatusSocket.receive.", ex);
102             }
103         }
104     }
105 
106     /***
107      * Allocate resources, create and start the thread.
108      * Resources are deallocated in <code>cleanup</code>.
109      * 
110      * @throws ServerException thrown if the server status socket could not be allocated.
111      */
112     public void start() throws ServerException {
113         try {
114             serverStatusSocket =
115                 new DatagramSocket(
116                     SPELLCAST_NET_SERVERLIST_PORT,
117                     InetAddress.getByName("0.0.0.0"));
118             serverStatusSocket.setSoTimeout(SOCKET_TIMEOUT);
119             logger_net.debug(
120                 "Status Socket = "
121                     + serverStatusSocket.getLocalAddress().getHostAddress()
122                     + ":"
123                     + serverStatusSocket.getLocalPort());
124 
125             isRunning = true;
126             serverStatusThread = new Thread(this, "serverStatusThread");
127             serverStatusThread.start();
128         }
129         catch (Exception e) {
130             throw new ServerException(e);
131         }
132     }
133 
134     /***
135      * Sets the isRunning flag to false which lets <code>run</code> exit the 
136      * infite loop. This method will wait until the thread dies before 
137      * calling <code>cleanup</code> where resources are deallocated.
138      */
139     public void stop() {
140         isRunning = false;
141         try {
142             serverStatusThread.join();
143         }
144         catch (InterruptedException e) {
145             logger.error("Unexpectedly Interrupted.", e);
146         }
147         cleanup();
148     }
149 
150     /***
151      * Dellocate resources allocated in <code>start</code>
152      */
153     private void cleanup() {
154         if (serverStatusSocket != null) {
155             serverStatusSocket.close();
156             serverStatusSocket = null;
157         }
158         serverStatusThread = null;
159     }
160 }