/interpreter/tags/at2dist220411/src/edu/vub/at/actors/net/comm/MulticastListenerThread.java

http://ambienttalk.googlecode.com/ · Java · 226 lines · 105 code · 26 blank · 95 comment · 17 complexity · 81302f7a4401aab5ca11cdc1cfb64010 MD5 · raw file

  1. /**
  2. * AmbientTalk/2 Project
  3. * (c) Programming Technology Lab, 2006 - 2007
  4. * Authors: Tom Van Cutsem & Stijn Mostinckx
  5. *
  6. * Permission is hereby granted, free of charge, to any person
  7. * obtaining a copy of this software and associated documentation
  8. * files (the "Software"), to deal in the Software without
  9. * restriction, including without limitation the rights to use,
  10. * copy, modify, merge, publish, distribute, sublicense, and/or
  11. * sell copies of the Software, and to permit persons to whom the
  12. * Software is furnished to do so, subject to the following
  13. * conditions:
  14. *
  15. * The above copyright notice and this permission notice shall be
  16. * included in all copies or substantial portions of the Software.
  17. *
  18. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  19. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  20. * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  21. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  22. * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  23. * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  24. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  25. * OTHER DEALINGS IN THE SOFTWARE.
  26. */
  27. package edu.vub.at.actors.net.comm;
  28. import edu.vub.at.util.logging.Logging;
  29. import java.io.DataOutputStream;
  30. import java.io.IOException;
  31. import java.net.DatagramPacket;
  32. import java.net.InetAddress;
  33. import java.net.MulticastSocket;
  34. import java.net.Socket;
  35. import java.net.SocketException;
  36. /**
  37. * This thread is responsible for listening for the heartbeats of other VMs
  38. * and for setting up the connection with a newly discovered VM.
  39. *
  40. * The heartbeat of a remote VM is the {@link Address} on which its {@link MasterConnectionThread}
  41. * is currently listening for incoming connections. When this thread hears such
  42. * an address (different from its own, and in the same AmbientTalk overlay network)
  43. * that address is compared to its own address. This comparison entitles one
  44. * VM address to be the <b>master</b> and one VM address to be the <b>slave</b>.
  45. *
  46. * The rule is that the slave is always responsible for establishing an explicit
  47. * connection with the master. If this VM is the slave, a connection is opened
  48. * to the remote VM's {@link MasterConnectionThread} and that socket connection
  49. * is registered with the {@link CommunicationBus}. If this VM is a master,
  50. * it does nothing at all (except for updating the time the slave VM was last seen),
  51. * knowing that the slave will contact him via the {@link MasterConnectionThread}.
  52. * In that case, it is the socket connection defined in that thread which will
  53. * be registered with the {@link CommunicationBus}.
  54. *
  55. * @author jededeck
  56. * @author tvcutsem
  57. */
  58. public class MulticastListenerThread extends Thread {
  59. /**
  60. * marked volatile such that all threads see the actual value of the variable,
  61. * rather than a cached copy (in a local register) which may contain a stale value
  62. */
  63. private volatile boolean isActive_ = true;
  64. /** the current network address of my communication bus */
  65. private final Address myHostAddress_;
  66. private volatile MulticastSocket socket_;
  67. private final CommunicationBus communicationBus_;
  68. public MulticastListenerThread(CommunicationBus owner, Address myHostAddress) {
  69. super("Multicast Listener for " + owner);
  70. communicationBus_ = owner;
  71. myHostAddress_ = myHostAddress;
  72. }
  73. /**
  74. * Gracefully shut down the thread. Note that even after this method is invoked,
  75. * the multicast listener may still accept an incoming heartbeat. Hence, care
  76. * has to be taken in the communication bus that any incoming calls from this
  77. * thread are ignored once this method has been invoked.
  78. */
  79. public void stopListening() {
  80. isActive_ = false;
  81. // close the socket to ensure that no more incoming heartbeats are received,
  82. // and to ensure that the listener does not remain blocked on the incoming
  83. // socket forever
  84. if (socket_ != null) {
  85. socket_.close();
  86. }
  87. }
  88. public void run() {
  89. try {
  90. DatagramPacket packet;
  91. byte[] addressBuffer = new byte[Address.MAX_ADDRESS_BYTE_SIZE];
  92. while (isActive_) {
  93. try {
  94. // if socket is not yet initialized, initialize it now
  95. if (socket_ == null) {
  96. socket_ = new MulticastSocket(MulticastServerThread.MC_PORT);
  97. socket_.joinGroup(MulticastServerThread.MC_ADDR);
  98. }
  99. // try to receive a broadcast heartbeat
  100. packet = new DatagramPacket(addressBuffer, addressBuffer.length);
  101. socket_.receive(packet);
  102. processIncomingAddress(packet.getData());
  103. } catch (SocketException e) {
  104. // considered fatal exception for the socket, so reset the socket
  105. Logging.Network_LOG.warn(toString() + ": could not create socket to master:" + e.getMessage());
  106. socket_ = null;
  107. } catch (IOException e) {
  108. Logging.Network_LOG.warn(toString() + ": error receiving heartbeat:" + e.getMessage());
  109. }
  110. }
  111. // getting here means the listener thread is asked to stop
  112. } finally {
  113. if (socket_ != null) {
  114. socket_.close();
  115. }
  116. Logging.Network_LOG.debug(toString() + " shutting down");
  117. }
  118. }
  119. /**
  120. * An address was received via the multicast socket.
  121. * If the address is not my own address, and it is an address from the same
  122. * AmbientTalk overlay network, update the time the address was last seen.
  123. * If it is a new address, register it with the communication bus and
  124. * determine whether this VM should play the role of master or slave in
  125. * establishing a new connection.
  126. */
  127. private void processIncomingAddress(byte[] receivedData) throws IOException {
  128. Address receivedAddress = Address.fromBytes(receivedData);
  129. // if the address is not of the same AmbientTalk network, disregard it
  130. if (!myHostAddress_.inSameNetwork(receivedAddress)) {
  131. Logging.Network_LOG.debug("Ignored incoming address of other network: " + receivedAddress);
  132. return;
  133. }
  134. // did I hear myself?
  135. if (myHostAddress_.equals(receivedAddress)) {
  136. return;
  137. }
  138. //Logging.Network_LOG.debug("Updated time last seen: " + receivedAddress);
  139. // try to update the time that this address was last seen
  140. boolean alreadyRegistered = communicationBus_.updateTimeLastSeen(receivedAddress);
  141. // detected a new VM: open connection to this VM
  142. if (!alreadyRegistered) {
  143. if (isMaster(receivedAddress)) {
  144. Logging.Network_LOG.debug("Master VM detected: " + receivedAddress);
  145. // the other VM is the master: I am the slave, so I should connect to him
  146. connectSlaveToMaster(receivedAddress);
  147. } else {
  148. Logging.Network_LOG.debug("Slave VM detected: " + receivedAddress);
  149. }
  150. }
  151. }
  152. /**
  153. * Check whether the given address is 'larger' than my address. If so,
  154. * the VM denoted by this address is the master and I am slave.
  155. */
  156. private boolean isMaster(Address receivedAddress) {
  157. int value = myHostAddress_.compareTo(receivedAddress);
  158. if (value > 0)
  159. return true;
  160. if (value == 0)
  161. throw new RuntimeException("Could not determine master because address " +
  162. myHostAddress_ + " is equal to " + receivedAddress);
  163. return false;
  164. }
  165. /**
  166. * When two VMs detect one another, both VMs independently decide which one plays
  167. * the role of master and which one plays the role of slave. The slave is responsible
  168. * for opening up a connection to the master and for sending its own address explicitly
  169. * to the master. When the master receives the address, it will in turn register a connection
  170. * with the slave VM.
  171. */
  172. private void connectSlaveToMaster(Address masterAddress) {
  173. InetAddress masterIp = masterAddress.ipAddress_;
  174. int masterPort = masterAddress.port_;
  175. Socket master = null;
  176. try {
  177. // connect to the master
  178. master = new Socket(masterIp, masterPort);
  179. // send my own address
  180. DataOutputStream dout = new DataOutputStream(master.getOutputStream());
  181. byte[] addr = myHostAddress_.serializedForm_;
  182. dout.writeInt(addr.length);
  183. dout.write(addr);
  184. dout.flush();
  185. // ONLY add a connection to the bus if everything went OK so far
  186. // this will also spawn a new command processor dedicated for handling the command objects received from the master
  187. communicationBus_.addConnection(masterAddress, master);
  188. } catch (IOException e) {
  189. Logging.Network_LOG.warn(toString() + ": error setting up connection with master: " + e.getMessage());
  190. try {
  191. if (master != null) {
  192. master.close();
  193. }
  194. } catch (IOException ioe) { }
  195. }
  196. }
  197. public String toString() {
  198. return super.getName();
  199. }
  200. }