/interpreter/tags/at2dist220411/src/edu/vub/at/actors/net/comm/MulticastListenerThread.java
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 */ 27package edu.vub.at.actors.net.comm; 28import edu.vub.at.util.logging.Logging; 29 30import java.io.DataOutputStream; 31import java.io.IOException; 32import java.net.DatagramPacket; 33import java.net.InetAddress; 34import java.net.MulticastSocket; 35import java.net.Socket; 36import java.net.SocketException; 37 38/** 39 * This thread is responsible for listening for the heartbeats of other VMs 40 * and for setting up the connection with a newly discovered VM. 41 * 42 * The heartbeat of a remote VM is the {@link Address} on which its {@link MasterConnectionThread} 43 * is currently listening for incoming connections. When this thread hears such 44 * an address (different from its own, and in the same AmbientTalk overlay network) 45 * that address is compared to its own address. This comparison entitles one 46 * VM address to be the <b>master</b> and one VM address to be the <b>slave</b>. 47 * 48 * The rule is that the slave is always responsible for establishing an explicit 49 * connection with the master. If this VM is the slave, a connection is opened 50 * to the remote VM's {@link MasterConnectionThread} and that socket connection 51 * is registered with the {@link CommunicationBus}. If this VM is a master, 52 * it does nothing at all (except for updating the time the slave VM was last seen), 53 * knowing that the slave will contact him via the {@link MasterConnectionThread}. 54 * In that case, it is the socket connection defined in that thread which will 55 * be registered with the {@link CommunicationBus}. 56 * 57 * @author jededeck 58 * @author tvcutsem 59 */ 60public class MulticastListenerThread extends Thread { 61 62 /** 63 * marked volatile such that all threads see the actual value of the variable, 64 * rather than a cached copy (in a local register) which may contain a stale value 65 */ 66 private volatile boolean isActive_ = true; 67 68 /** the current network address of my communication bus */ 69 private final Address myHostAddress_; 70 71 private volatile MulticastSocket socket_; 72 73 private final CommunicationBus communicationBus_; 74 75 public MulticastListenerThread(CommunicationBus owner, Address myHostAddress) { 76 super("Multicast Listener for " + owner); 77 communicationBus_ = owner; 78 myHostAddress_ = myHostAddress; 79 } 80 81 /** 82 * Gracefully shut down the thread. Note that even after this method is invoked, 83 * the multicast listener may still accept an incoming heartbeat. Hence, care 84 * has to be taken in the communication bus that any incoming calls from this 85 * thread are ignored once this method has been invoked. 86 */ 87 public void stopListening() { 88 isActive_ = false; 89 // close the socket to ensure that no more incoming heartbeats are received, 90 // and to ensure that the listener does not remain blocked on the incoming 91 // socket forever 92 if (socket_ != null) { 93 socket_.close(); 94 } 95 } 96 97 public void run() { 98 try { 99 DatagramPacket packet; 100 byte[] addressBuffer = new byte[Address.MAX_ADDRESS_BYTE_SIZE]; 101 102 while (isActive_) { 103 try { 104 105 // if socket is not yet initialized, initialize it now 106 if (socket_ == null) { 107 socket_ = new MulticastSocket(MulticastServerThread.MC_PORT); 108 socket_.joinGroup(MulticastServerThread.MC_ADDR); 109 } 110 111 // try to receive a broadcast heartbeat 112 packet = new DatagramPacket(addressBuffer, addressBuffer.length); 113 socket_.receive(packet); 114 115 processIncomingAddress(packet.getData()); 116 } catch (SocketException e) { 117 // considered fatal exception for the socket, so reset the socket 118 Logging.Network_LOG.warn(toString() + ": could not create socket to master:" + e.getMessage()); 119 socket_ = null; 120 } catch (IOException e) { 121 Logging.Network_LOG.warn(toString() + ": error receiving heartbeat:" + e.getMessage()); 122 } 123 } 124 125 // getting here means the listener thread is asked to stop 126 } finally { 127 if (socket_ != null) { 128 socket_.close(); 129 } 130 Logging.Network_LOG.debug(toString() + " shutting down"); 131 } 132 } 133 134 /** 135 * An address was received via the multicast socket. 136 * If the address is not my own address, and it is an address from the same 137 * AmbientTalk overlay network, update the time the address was last seen. 138 * If it is a new address, register it with the communication bus and 139 * determine whether this VM should play the role of master or slave in 140 * establishing a new connection. 141 */ 142 private void processIncomingAddress(byte[] receivedData) throws IOException { 143 Address receivedAddress = Address.fromBytes(receivedData); 144 145 // if the address is not of the same AmbientTalk network, disregard it 146 if (!myHostAddress_.inSameNetwork(receivedAddress)) { 147 Logging.Network_LOG.debug("Ignored incoming address of other network: " + receivedAddress); 148 return; 149 } 150 151 // did I hear myself? 152 if (myHostAddress_.equals(receivedAddress)) { 153 return; 154 } 155 156 //Logging.Network_LOG.debug("Updated time last seen: " + receivedAddress); 157 158 // try to update the time that this address was last seen 159 boolean alreadyRegistered = communicationBus_.updateTimeLastSeen(receivedAddress); 160 161 // detected a new VM: open connection to this VM 162 if (!alreadyRegistered) { 163 if (isMaster(receivedAddress)) { 164 Logging.Network_LOG.debug("Master VM detected: " + receivedAddress); 165 // the other VM is the master: I am the slave, so I should connect to him 166 connectSlaveToMaster(receivedAddress); 167 } else { 168 Logging.Network_LOG.debug("Slave VM detected: " + receivedAddress); 169 } 170 } 171 } 172 173 /** 174 * Check whether the given address is 'larger' than my address. If so, 175 * the VM denoted by this address is the master and I am slave. 176 */ 177 private boolean isMaster(Address receivedAddress) { 178 int value = myHostAddress_.compareTo(receivedAddress); 179 if (value > 0) 180 return true; 181 if (value == 0) 182 throw new RuntimeException("Could not determine master because address " + 183 myHostAddress_ + " is equal to " + receivedAddress); 184 return false; 185 } 186 187 /** 188 * When two VMs detect one another, both VMs independently decide which one plays 189 * the role of master and which one plays the role of slave. The slave is responsible 190 * for opening up a connection to the master and for sending its own address explicitly 191 * to the master. When the master receives the address, it will in turn register a connection 192 * with the slave VM. 193 */ 194 private void connectSlaveToMaster(Address masterAddress) { 195 InetAddress masterIp = masterAddress.ipAddress_; 196 int masterPort = masterAddress.port_; 197 Socket master = null; 198 199 try { 200 // connect to the master 201 master = new Socket(masterIp, masterPort); 202 203 // send my own address 204 DataOutputStream dout = new DataOutputStream(master.getOutputStream()); 205 byte[] addr = myHostAddress_.serializedForm_; 206 dout.writeInt(addr.length); 207 dout.write(addr); 208 dout.flush(); 209 210 // ONLY add a connection to the bus if everything went OK so far 211 // this will also spawn a new command processor dedicated for handling the command objects received from the master 212 communicationBus_.addConnection(masterAddress, master); 213 } catch (IOException e) { 214 Logging.Network_LOG.warn(toString() + ": error setting up connection with master: " + e.getMessage()); 215 try { 216 if (master != null) { 217 master.close(); 218 } 219 } catch (IOException ioe) { } 220 } 221 } 222 223 public String toString() { 224 return super.getName(); 225 } 226}