/*
* (C) Copyright IBM Corp. 2011
*
* LICENSE: Eclipse Public License v1.0
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.ibm.gaiandb.clientseeker;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* Uses multicast to discover the nearest Gaian node and return its details in the form of a descriptor.
* This descriptor can then be used by other components in order to connect to a Fabric Registry.
*
* Note: this implementation makes use of Gaian clusters - essentially private communities of Gaian
* nodes. Each cluster will only respond to specific requests to join that cluster (the cluster id
* is a private identifier). This should mean that only Gaian nodes in the "FabricRegistry" cluster
* will respond - however, limitations in the current alphaWorks release of Gaian (v.1.03) could
* mean that other non-Registry Gaian nodes respond.
*
* @author DavidBarker
*
*/
public class RegistrySeeker extends AbstractSeeker {
// Use PROPRIETARY notice if class contains a main() method, otherwise use COPYRIGHT notice.
public static final String COPYRIGHT_NOTICE = "(c) Copyright IBM Corp. 2011";
/**
* Request message to send for Gaian.
*
* Format:
*
* REQ <my hostname> <my ip> <args - for our purposes, cluster id>
*
*/
private String GAIAN_REQ_MESSAGE = "REQ @@hostname@@ @@ip@@ FabricRegistry";
public RegistrySeeker() {
/** TODO - make multicast group and port configurable */
this.request_group = "230.255.255.255";
this.request_port = 7777;
}
/**
* Discover a Fabric Registry using multicast and return its details in the form of a descriptor.
*
* @throws ResourceNotFoundException if no (valid) responses are received from any Gaian nodes.
*/
public RegistryDescriptor discoverFabricRegistry(long waitMillis) throws IOException, ResourceNotFoundException {
DiscoveryListener grl = null;
int discoveredHostPacketIndex = -1;
try {
/* get the local IP address information for the host where this code is running */
getLocalIPInformation();
/* start the multicast listener in a separate thread */
grl = new DiscoveryListener();
grl.init();
new Thread(grl,"DiscoveryListener").start();
/*
* Build the request payload
*/
String request = GAIAN_REQ_MESSAGE.replaceFirst("@@ip@@", request_interface_address);
/*
* Send the request
*/
publishRequest(request);
/* wait for a response from any Gaian nodes that are out there */
waitForResponse(waitMillis);
/* shutdown listener etc. */
cleanupResources(grl);
/* remove any invalid responses */
pruneResponses();
/* if we didn't get any valid responses, bail out */
if (responses.size() == 0) {
throw new ResourceNotFoundException("Failed to discover a Fabric Registry using interface with IP " + request_interface_address);
} else {
/* look for a local response (if one exists) */
for (DatagramPacket acknowledgement: responses) {
/* preferably want the local node (if such exists) */
if (responseIsLocal(acknowledgement.getAddress().getHostAddress())) {
discoveredHostPacketIndex = responses.indexOf(acknowledgement);
}
}
if (discoveredHostPacketIndex == -1) { /* no local host - arbitrarily pick the first one instead */
System.out.println("A local Fabric Registry was not found - using first respondent instead.");
discoveredHostPacketIndex = 0;
}
}
} catch (UnknownHostException e1) {
throw new IOException("Unknown host encountered. See nested exception for details.", e1);
} finally { // tidy up resources
cleanupResources(grl);
}
/* return a descriptor containing the information required to connect */
DatagramPacket p = responses.get(discoveredHostPacketIndex);
return new GaianRegistryDescriptorImpl(p.getAddress().getHostAddress());
}
/**
* Process the received packets list, removing duplicates and any packets
* other than the ACKs that are of interest.
*/
private void pruneResponses() {
// System.out.println("Responses before pruning: " + responses.size());
List<String> seenHosts = new ArrayList<String>();
synchronized (responses) {
Iterator<DatagramPacket> response_it = responses.iterator();
/* Check each node, if we've not seen a node for more than the node_timeout, delete it */
DatagramPacket response = null;
String payload = null;
while(response_it.hasNext())
{
response = response_it.next();
//System.out.println("response host: " + response.getAddress().getHostAddress());
payload = new String(response.getData(),0,response.getLength());
/* only interested in ACK responses */
if (payload.startsWith("ACK")) {
/* chop it up and check the version - needs to be 1.03 or greater */
String[] ackParts = payload.split(" ");
if (ackParts.length == 6 && (! ackParts[5].startsWith("1.0"))) {
/* wrong version - remove */
response_it.remove();
} else if (ackParts.length < 6) { /* short ACK from an old version - remove */
response_it.remove();
} else { /* valid - ignore any other packets from same host */
if (! seenHosts.contains(response.getAddress().getHostAddress())) {
/* add to seen list */
seenHosts.add(response.getAddress().getHostAddress());
} else { /* duplicate packet - ignore */
response_it.remove();
}
}
} else { /* not an ACK - remove it from the list */
response_it.remove();
}
}
}
// System.out.println("Responses after pruning: " + responses.size());
}
}