/*
* (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.InetAddress;
import java.net.MulticastSocket;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Super-class for client/platform auto-discovery within the Fabric.
* Use the appropriate subclass depending on whether attempting to discover a Fabric
* node or a Fabric Registry.
*
* @author DavidBarker
*
*/
public class 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";
/** the multicast/broadcast address to use for discovery */
protected String request_group = null;
/** the multicast port to use */
protected int request_port = 0;
/** local ip address */
protected String request_interface_address = null;
/** local hostname */
protected String local_host = null;
/**
* Socket used to send the request packet.
*/
protected MulticastSocket udpSocket = null;
/**
* InetAddress object representing the multicast/broadcast address.
*/
protected InetAddress groupAddr = null;
/**
* InetAddress for the local interface used to publish the request.
*/
protected InetAddress interfaceAddr = null;
/**
* List containing the payloads of each response.
*/
protected List<DatagramPacket> responses = new ArrayList<DatagramPacket>();
/**
* Checks whether the repondent IP address is local by getting the complete
* list of local addresses for all interfaces and looking for a match.
*
* @param ipAddress
* @return true if the address is assigned to a local interface.
*/
protected boolean responseIsLocal(String ipAddress) {
String[] localAddresses;
try {
localAddresses = NetworkUtils.getLocalAddressStrings();
if (localAddresses != null) {
List<String> addressList = Arrays.asList(localAddresses);
if (addressList.contains(ipAddress)) {
return true;
}
}
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SocketException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return false;
}
/**
* Get details of the localhost.
*
* @throws UnknownHostException
*/
protected void getLocalIPInformation() throws UnknownHostException {
InetAddress localAddress = InetAddress.getLocalHost();
request_interface_address = localAddress.getHostAddress();
local_host = localAddress.getHostName();
}
/**
* Publish a request message to the specified multicast group.
*/
protected void publishRequest(String message) throws IOException, SocketException {
groupAddr = InetAddress.getByName(request_group);
interfaceAddr = InetAddress.getByName(request_interface_address);
/* is it a multicast address? */
if (groupAddr.isMulticastAddress()) {
/* open the socket and join the multicast group */
udpSocket = new MulticastSocket();
udpSocket.setNetworkInterface(NetworkInterface.getByInetAddress(interfaceAddr));
udpSocket.setInterface(interfaceAddr);
udpSocket.joinGroup(groupAddr);
/* Send request packet */
DatagramPacket p = new DatagramPacket(message.getBytes(),message.getBytes().length, groupAddr,7777);
System.out.println("Sending request: " + new String(p.getData(),0,p.getLength()));
udpSocket.send(p);
} else {
System.err.println("Invalid multicast address: " + groupAddr.toString());
}
}
/**
* Wait the specified number of milliseconds for multicast packets to arrive.
*
* @param waitMillis - the number of milliseconds to wait (sleep).
* @throws IOException if no packets arrive in the specified time.
*/
protected void waitForResponse(long waitMillis) throws ResourceNotFoundException {
if (waitMillis <= 0) { /* wait for default time - 5 seconds */
waitMillis = 5000;
}
try {
Thread.sleep(waitMillis);
} catch (InterruptedException e) {
/* sleep interrupted */
}
System.out.println("Responses: " + responses.size());
if (responses.size() == 0) { /* Nobody out there or taking too long to respond */
throw new ResourceNotFoundException("No responses within specified timeout: " + waitMillis);
}
}
/**
* Shutdown the listening and publishing resources used for the discovery, as appropriate.
*
* @param grl
*/
protected void cleanupResources(DiscoveryListener grl) {
if (grl != null) { // stop listener
grl.stop();
}
if (udpSocket != null) {
udpSocket.close();
}
}
/*
* Classes
*/
/**
* Class used to listen for discovery responses.
*
* @author DavidBarker
*
*/
class DiscoveryListener implements Runnable {
/** Flag used to indicate when the main thread should terminate */
private boolean isRunning = false;
/** Multicast socket used to listen for discovery requests */
private MulticastSocket udpListenRequestSocket = null;
private InetAddress groupListenRequestAddr = null;
private InetAddress interfaceListenRequestAddr = null;
private String listenPayload = null;
/**
* Initialise the listener, including opening a multicast socket and joining
* the specified group.
*
* @throws IOException if the socket cannot be opened.
* @throws SocketException if there is a problem setting the interface or joining the multicast group.
*/
public void init() throws IOException, SocketException {
String request_interface_address = InetAddress.getLocalHost().getHostAddress();
groupListenRequestAddr = InetAddress.getByName(request_group);
interfaceListenRequestAddr = InetAddress.getByName(request_interface_address);
/* is it a multicast address? */
if (groupListenRequestAddr.isMulticastAddress()) {
udpListenRequestSocket = new MulticastSocket(request_port);
udpListenRequestSocket.setInterface(interfaceListenRequestAddr);
udpListenRequestSocket.joinGroup(groupListenRequestAddr);
} else {
System.err.println("Not a valid multicast address!");
}
}
/**
* Main processing method which handles received datagrams.
*/
public void run() {
isRunning = true;
while (isRunning) {
try {
/* TODO confirm packet size is appropriate */
byte[] buf = new byte[256];
DatagramPacket p = new DatagramPacket(buf, buf.length);
try {
if (! udpListenRequestSocket.isClosed()) { /* if socket not already closed */
udpListenRequestSocket.receive(p);
/* get the packet as a string */
listenPayload = new String(p.getData(),0,p.getLength());
/* add unique payloads - subclass responsibility to identify valid responses */
if (! responses.contains(listenPayload)) {
responses.add(p);
}
}
} catch (SocketTimeoutException e) {
// TODO what happens if we don't hear from anybody?
}
} catch (IOException e) {
if (!udpListenRequestSocket.isClosed()) {
e.printStackTrace();
}
/* stop looping */
break;
}
}
}
/**
* Stop listening.
*/
public void stop() {
isRunning = false;
if (udpListenRequestSocket != null) {
udpListenRequestSocket.close();
}
}
}
}