package fr.dgac.ivy ; import java.lang.Thread; import java.net.*; import java.io.*; import java.util.StringTokenizer; import gnu.regexp.*; import java.util.Vector; /** * A private Class for the Ivy rendezvous * * @author François-Régis Colin * @author Yannick Jestin * @author http://www.tls.cena.fr/products/ivy/ * * right now, the rendez vous is on a UDP socket. The watcher will answer to * each peer advertising its arrival on the bus. The intrinsics of Unix are so * that the broadcast is done using the same socket, which is not a good * thing. */ class IvyWatcher implements Runnable { private static boolean debug = (System.getProperty("IVY_DEBUG")!=null); private Vector domainaddrList; private boolean watcherrunning = false; private boolean isMulticastAddress = false; private Thread broadcastListener ; private Ivy bus; /* master bus controler */ private DatagramSocket broadcast; /* supervision socket */ // it can also be a MulticastSocket, which inherits from the previous /** * creates an Ivy watcher. * @param bus the bus */ IvyWatcher(Ivy bus) throws IvyException { this.bus = bus; domainaddrList = new Vector(); } /** * the behaviour of the thread watching the UDP socket. * this thread will stop either when the bus stops or when the * watcherrunning will be set to false * * TODO: better handling of exceptions, because we juste System.err.println * here, run cannot throw IvyException ... */ public void run() { byte buf[] = new byte[256]; DatagramPacket packet=new DatagramPacket(buf, 256); int port; traceDebug("IvyWatcher waiting for Broadcast"); while( watcherrunning && bus.ivyRunning ) try { broadcast.receive(packet); String msg = new String(packet.getData()) ; InetAddress remotehost = packet.getAddress(); traceDebug("BUSWATCHER Receive Broadcast from "+ remotehost.getHostName()+":"+packet.getPort()); // check if remoteaddr is in our broadcast domain list otherwise we ignore the broadcast if ( !isInDomain( remotehost ) ) continue; StringTokenizer st = new StringTokenizer(msg); if ( !st.hasMoreTokens()) { System.err.println("Bad format "+msg); continue; } int version = Integer.parseInt( st.nextToken() ); if ( version != bus.PROCOCOLVERSION ) { System.err.println("Ignoring bad protocol version broadcast"); continue; } if ( ! st.hasMoreTokens()) { System.err.println("Bad format "+msg); continue; } port = Integer.parseInt( st.nextToken() ); if ( (bus.applicationPort == port) ) continue; traceDebug("BUSWATCHER Broadcast de " +packet.getAddress().getHostName() +":"+packet.getPort()+" port "+port+" version "+version); try { Socket socket = new Socket( remotehost, port ); bus.addClient(socket,false); } catch ( UnknownHostException e ) { System.err.println("Unkonwn host "+remotehost + e.getMessage()); } catch ( IOException e) { System.err.println("can't connect to "+remotehost+" port "+ port+e.getMessage()); } } catch (java.io.InterruptedIOException jii ){ if (!watcherrunning) break; } catch (java.io.IOException ioe ){ System.err.println("IvyWatcher IOException "+ ioe.getMessage() ); } broadcast.close(); traceDebug("broadcast listener normal shutdown"); } // while /** * stops the thread waiting on the broadcast socket */ void stop() { watcherrunning=false; } private void sendBroadcast(String data, String net) throws IvyException { try { // simple trick to expand to 255 (Alex Bustico) net += ".255.255.255"; RE exp = new RE( "^(\\d+\\.\\d+\\.\\d+\\.\\d+).*"); net = exp.substitute( net , "$1" ); } catch ( REException e ){ throw new IvyException("Bad broascat addr"); } try { InetAddress iaddr = InetAddress.getByName(net); domainaddrList.addElement(iaddr); DatagramPacket packet = new DatagramPacket( data.getBytes(), data.length(), iaddr, broadcast.getLocalPort() ); broadcast.send(packet); } catch ( UnknownHostException e ) { throw new IvyException("Broadcast sent on unknown network "+ e.getMessage()); } catch ( IOException e ) { throw new IvyException("Broadcast error " + e.getMessage() ); } } void start(String domain) throws IvyException { String domainaddr; // parse Domain to get port number int port; int sep_index = domain.lastIndexOf( ":" ); if ( sep_index == -1 ) { port = bus.DEFAULT_PORT; domainaddr = domain; } else { port = Integer.parseInt( domain.substring( sep_index +1 )); domainaddr = domain.substring(0,sep_index); } // Handling of multicast address try { InetAddress group = InetAddress.getByName(domainaddr); if (group.isMulticastAddress()) { isMulticastAddress = true; broadcast = new MulticastSocket(port ); // create the UDP socket ((MulticastSocket)broadcast).joinGroup(group); } else { broadcast = new MulticastSocket(port ); // create the UDP socket } } catch ( IOException e ) { throw new IvyException("IvyWatcher I/O error" + e ); } try { broadcast.setSoTimeout(100); } catch ( java.net.SocketException jns ) { throw new IvyException("IvyWatcher setSoTimeout error" + jns.getMessage() ); } // starts a Thread listening on the socket watcherrunning=true; broadcastListener = new Thread(this); broadcastListener.start(); // notifies our arrival on each domain: protocol version + port String hello = bus.PROCOCOLVERSION + " " + bus.applicationPort + "\n"; StringTokenizer st = new StringTokenizer(domainaddr," \t:,"); while ( st.hasMoreTokens()) { sendBroadcast( hello, st.nextToken() ); } } private boolean isInDomain( InetAddress host ){ byte rem_addr[] = host.getAddress(); for ( int i = 0 ; i < domainaddrList.size(); i++ ) { byte addr[] = ((InetAddress)domainaddrList.elementAt(i)).getAddress(); int j ; for ( j = 0 ; j < 4 ; j++ ) if ( (addr[j] != -1) && (addr[j] != rem_addr[j]) ) break; if ( j == 4 ) { traceDebug( "host " + host + " is in domain\n" ); return true; } } traceDebug( "host " + host + " Not in domain\n" ); return false; } private void traceDebug(String s){ if (debug) System.out.println("-->ivywatcher<-- "+s); } } // class IvyWatcher /* EOF */