PageRenderTime 21ms CodeModel.GetById 10ms app.highlight 7ms RepoModel.GetById 1ms app.codeStats 0ms

/interpreter/tags/at2dist160410/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 */
 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}