diff options
Diffstat (limited to 'src/Ivy.java')
-rwxr-xr-x | src/Ivy.java | 362 |
1 files changed, 236 insertions, 126 deletions
diff --git a/src/Ivy.java b/src/Ivy.java index 8756c94..5b423eb 100755 --- a/src/Ivy.java +++ b/src/Ivy.java @@ -1,8 +1,11 @@ -// -// API de connexion au bus logiciel ivy -// -// - +/** + * a software bus package + * + * @author François-Régis Colin + * @author Yannick Jestin + * @author <a href="http://www.tls.cena.fr/products/ivy/">http://www.tls.cena.fr/products/ivy/</a> + * + */ package fr.dgac.ivy ; import java.net.*; @@ -11,179 +14,270 @@ import java.util.Vector; import java.util.Hashtable; import java.util.StringTokenizer; +/** + * A class connecting to the Ivy software bus. + * For example: + *<pre> + *Ivy bus = new Ivy("Dummy agent","ready",null); + *bus.bindMsg("(.*)",myMessageListener); + *bus.start(null); + *</pre> + */ public class Ivy implements Runnable, IvyApplicationListener { - static private boolean debug = (System.getProperty("IVY_DEBUG")!=null) ; - + /** + * the name of the application on the bus + */ + public String appName; + /** + * the protocol version number + */ + public static final int PROCOCOLVERSION = 3 ; + /** + * the port for the UDP rendez vous, if none is supplied + */ public static final int DEFAULT_PORT = 2010 ; + /** + * the domain for the UDP rendez vous + */ public static final String DEFAULT_DOMAIN = "127.255.255.255:"+DEFAULT_PORT; - public String appName; - private boolean ivyRunning = false; - private String ready_message = null; + + private static boolean debug = (System.getProperty("IVY_DEBUG")!=null) ; + private static int serial=0; /* an unique ID for each regexps */ private ServerSocket app; private IvyWatcher watch; private Thread server; - - private Hashtable regexp_out = new Hashtable(); private Hashtable callbacks = new Hashtable(); private Vector clients = new Vector(); private Vector ivyApplicationListenerList = new Vector(); private String messages_classes[] = null; - private int myport; /* Application port number */ - - public synchronized boolean ivyRunning(){ return ivyRunning; } + int applicationPort; /* Application port number */ + boolean ivyRunning = false; + Hashtable regexp_out = new Hashtable(); + String ready_message = null; + + /** + * Readies the structures for the software bus connexion. + * + * All the dirty work is done un the start() method + * @see #start + * @param name The name of your Ivy agent on the software bus + * @param message The hellow message you will send once ready + * @param appcb A callback handling the notification of connexions and + * disconnections, may be null + */ public Ivy( String name, String message, IvyApplicationListener appcb) { appName = name; ready_message = message; if ( appcb != null ) ivyApplicationListenerList.addElement( appcb ); } - void classes( String msg_classes[] ) { messages_classes = msg_classes; } - - public Hashtable getRegexpOut() { return regexp_out; } - - public String getReadyMessage() { return ready_message; } - - void addClient( Socket socket ) throws IOException { - IvyClient client = new IvyClient( this, socket); - clients.addElement( client ); - } - - void removeClient( IvyClient client ) { - clients.removeElement( client ); + /** + * connects the Ivy bus to a domain or list of domains + * @param domainbus a domain of the form 10.0.0:1234, it is similar to the + * netmask without the trailing .255. This will determine the meeting point + * of the different applications. Right now, this is done with an UDP + * broadcast. Beware of routing problems ! You can also use a comma + * separated list of domains. + * + */ + public void start(String domainbus) throws IvyException { + try { + app = new ServerSocket(0); + applicationPort = app.getLocalPort(); + } catch (IOException e) { + throw new IvyException("can't open TCP service socket " + e ); + } + traceDebug("TCP service open on port "+applicationPort); + watch = new IvyWatcher(this); + ivyRunning = true; + server = new Thread(this); + server.start(); + watch.start(getDomain(domainbus)); } - void callCallback(IvyClient client, Integer key, String msgarg) { - IvyMessageListener callback=(IvyMessageListener)callbacks.get(key); - if (callback==null){ - traceDebug("(callCallback) Not regexp matching id "+key.intValue()); - return; + /** + * disconnects from the Ivy bus. + */ + public void stop() { + try { + ivyRunning = false; + watch.stop(); + app.close(); + for ( int i = 0 ; i < clients.size(); i++ ) { + IvyClient client = (IvyClient)clients.elementAt(i); + client.close("normal Ivy Stopping..."); + // advertise that this is a normal + //close for debugging purposes + } + } catch (IOException e) { + traceDebug("IOexception Stop "); } - StringTokenizer st = new StringTokenizer(msgarg,IvyClient.EndArg); - String args[] = new String[st.countTokens()]; - int i=0; - while (st.hasMoreTokens()){ args[i++] = st.nextToken();} - callback.receive( client, args ); + clients.removeAllElements(); } - public int getApplicationPort() { return myport; } - + /** + * Performs a pattern matching according to everyone's regexps, and sends + * the results to the relevant ivy agents. + * <p><em>There is one thread for each client connected, we could also + * create another thread each time we send a message.</em> + * @param message A String which will be compared to the regular + * expressions of the different clients + * @return the number of messages actually sent + */ public int sendMsg( String message ) { - int count = 0; - // envoie le message à tous les clients du bus - // TODO: il faudrait mettre une Thread emission par client */ + int count = 0; + // TODO: il faudrait mettre une Thread emission par client */ for ( int i = 0 ; i < clients.size(); i++ ) { IvyClient client = (IvyClient)clients.elementAt(i); count += client.sendMsg( message ); } - return count; + return count; } - static private int serial=0; + /** + * Subscribes to a regular expression. + * + * The callback will be executed with + * the saved parameters of the regexp as arguments when a message will sent + * by another agent. A program <em>doesn't</em> receive its own messages. + * <p>Example: + * <br>the Ivy agent A performs <pre>b.bindMsg("^Hello (*)",cb);</pre> + * <br>the Ivy agent B performs <pre>b2.sendMsg("Hello world");</pre> + * <br>a thread in A will uun the callback cb with its second argument set + * to a array of String, with one single element, "world" + * @param regexp a perl regular expression, groups are done with parenthesis + * @param callback any objects implementing the IvyMessageListener + * interface, on the AWT/Swing framework + * @return the id of the regular expression + */ public int bindMsg(String regexp, IvyMessageListener callback ) { - // associe une nouvelle regexp à un nouveau callback + // creates a new binding (regexp,callback) Integer key = new Integer(serial++); regexp_out.put(key,regexp); callbacks.put(key,callback ); - // indique aux autres clients la nouvelle regexp + // notifies the other clients this new regexp for (int i=0;i<clients.size();i++){ ((IvyClient)clients.elementAt(i)).sendRegexp(key.intValue(),regexp); } return key.intValue(); } - public void unBindMsg(int id) { + /** + * unsubscribes a regular expression + * + * @param id the id of the regular expression, returned when it was bound + */ + public void unBindMsg(int id) throws IvyException { Integer key = new Integer(id); if ( ( regexp_out.remove(key) == null ) || (callbacks.remove(key) == null ) ) { - System.err.println("attention j'enlève une regexp inexistante"); + throw new IvyException("client wants to remove an unexistant regexp "+id); } for (int i=0;i<clients.size();i++ ) { - ((IvyClient)clients.elementAt(i)).delRegexp(id ); + ((IvyClient)clients.elementAt(i)).delRegexp(id ); } } - /* gestion des applications listener et callback associe */ + + /** + * adds an application listener to a bus + * @param callback is an object implementing the IvyApplicationListener + * interface + * @return the id of the application listener, useful if you wish to remove + * it later + */ public int addApplicationListener(IvyApplicationListener callback){ - // associe une nouvelle regexp à un nouveau callback - ivyApplicationListenerList.addElement( callback ); + ivyApplicationListenerList.addElement(callback); int id = ivyApplicationListenerList.indexOf( callback ); return id; } - public void removeApplicationListener( int id ) { - ivyApplicationListenerList.removeElementAt(id); + + /** + * removes an application listener + * @param id the id of the application listener to remove + */ + public void removeApplicationListener(int id){ + ivyApplicationListenerList.removeElementAt(id); } + + /* invokes the application listeners upon arrival of a new Ivy client + * it *might* be considered poor style to invoke them as the same level + * as the others applicationListeners. This is part of the interface + */ public void connect(IvyClient client){ - for ( int i = 0 ; i < ivyApplicationListenerList.size(); i++ ) - { - IvyApplicationListener listener = (IvyApplicationListener)ivyApplicationListenerList.elementAt(i); - listener.connect(client); - } + for ( int i = 0 ; i < ivyApplicationListenerList.size(); i++ ) { + ((IvyApplicationListener)ivyApplicationListenerList.elementAt(i)).connect(client); + } } + + /* invokes the application listeners upon departure of an Ivy client + * ibid. + */ public void disconnect(IvyClient client){ - for ( int i = 0 ; i < ivyApplicationListenerList.size(); i++ ) - { - IvyApplicationListener listener = (IvyApplicationListener)ivyApplicationListenerList.elementAt(i); - listener.disconnect(client); - } + for ( int i = 0 ; i < ivyApplicationListenerList.size(); i++ ) { + ((IvyApplicationListener)ivyApplicationListenerList.elementAt(i)).disconnect(client); + } } + + /* invokes the application listeners upon death of an Ivy client + * ibid + */ public void die(IvyClient client, int id){ - for ( int i = 0 ; i < ivyApplicationListenerList.size(); i++ ) - { - IvyApplicationListener listener = (IvyApplicationListener)ivyApplicationListenerList.elementAt(i); - listener.die(client, id); - } + for ( int i = 0 ; i < ivyApplicationListenerList.size(); i++ ) { + ((IvyApplicationListener)ivyApplicationListenerList.elementAt(i)).die(client, id); + } } + + /* invokes the direct message callbacks + * ibid + */ public void directMessage( IvyClient client, int id,String msgarg ){ - for ( int i = 0 ; i < ivyApplicationListenerList.size(); i++ ) - { - IvyApplicationListener listener = (IvyApplicationListener)ivyApplicationListenerList.elementAt(i); - listener.directMessage(client,id, msgarg); - } + for ( int i = 0 ; i < ivyApplicationListenerList.size(); i++ ) { + ((IvyApplicationListener)ivyApplicationListenerList.elementAt(i)).directMessage(client,id, msgarg); + } } - public static String getDomain(String domainbus) throws IvyException { - if ( domainbus == null ) - domainbus = System.getProperty("IVYBUS"); - if ( domainbus == null ) - domainbus = DEFAULT_DOMAIN; - return domainbus; + /////////////////////////////////////////////////////////////////: + // + // Protected methods + // + /////////////////////////////////////////////////////////////////: + + void addClient(Socket socket,boolean peerCalling) throws IOException { + IvyClient client = new IvyClient(this, socket,peerCalling); + clients.addElement(client); } - public void start(String domainbus) throws IvyException { - try { - app = new ServerSocket(0); - myport = app.getLocalPort(); - } catch (IOException e) { - throw new IvyException("ne peut pas ouvrir la socket server" + e ); - } - /* Start watcher thread */ - watch = new IvyWatcher(this); - traceDebug("BUS TCP: "+myport); - ivyRunning = true; - server = new Thread( this); - /* Start ivy server thread */ - server.start(); - /* Send ivy helo msg */ - watch.sendStart(getDomain(domainbus)); + + void removeClient( IvyClient client ) { + clients.removeElement( client ); } - public void stop() { - try { - ivyRunning = false; - watch.close(); /* Stop watcher thread */ - app.close(); /* Stop ivy server thread */ - for ( int i = 0 ; i < clients.size(); i++ ) { - IvyClient client = (IvyClient)clients.elementAt(i); - client.close("Ivy Stopping...");/* Stop client thread */ - } - } catch ( IOException e ) { - traceDebug("ioexception Stop "); + void callCallback(IvyClient client, Integer key, String msgarg) throws IvyException { + IvyMessageListener callback=(IvyMessageListener)callbacks.get(key); + if (callback==null){ + throw new IvyException("(callCallback) Not regexp matching id "+key.intValue()); } - clients.removeAllElements(); + StringTokenizer st = new StringTokenizer(msgarg,IvyClient.EndArg); + String args[] = new String[st.countTokens()]; + int i=0; + while (st.hasMoreTokens()){ args[i++] = st.nextToken();} + callback.receive( client, args ); } - + + + static String getDomain(String domainbus) throws IvyException { + if ( domainbus == null ) domainbus = System.getProperty("IVYBUS"); + if ( domainbus == null ) domainbus = DEFAULT_DOMAIN; + return domainbus; + } + + + /* + * checks the "validity" of a regular expression. + * TODO i'm not sure this is still used by anything. + */ boolean CheckRegexp( String exp ) { - /* accepte tout par default */ boolean regexp_ok = true; if ( exp.startsWith( "^" )&&messages_classes!=null) { regexp_ok=false; @@ -194,7 +288,12 @@ public class Ivy implements Runnable, IvyApplicationListener { return regexp_ok; } - public boolean checkConnected( IvyClient clnt ) { + /* + * TODO prevents two clients from connecting to each other at the same time + * there is still a lingering bug here, that we could avoid with the + * SchizoToken. + */ + boolean checkConnected( IvyClient clnt ) { if ( clnt.getAppPort() == 0 ) return false; for ( int i = 0 ; i < clients.size(); i++ ) { IvyClient client = (IvyClient)clients.elementAt(i); @@ -203,25 +302,36 @@ public class Ivy implements Runnable, IvyApplicationListener { return false; } + /* + * the service socket thread reader. + */ public void run() { - Socket socket; - while( ivyRunning() ) { - try { - socket = app.accept(); - addClient( socket ); - } catch( IOException e ) { - /* Normal terminaison si socket close */ - /* TODO: et les autres */ - traceDebug("Error IvyServer exception: " + e.getMessage()); - } - } + while(ivyRunning){ + try { + Socket socket = app.accept(); + addClient(socket,true); // the peer called me + } catch( IOException e ) { + /* TODO is it a normal termination on socket close ? */ + traceDebug("Error IvyServer exception: " + e.getMessage()); + System.out.println("DEBUG TCP socket reader caught an exception " + e.getMessage()); + } + } + traceDebug("IvyServer end of trans"); } + + /////////////////////////////////////////////////////////////////: + // + // Private methods + // + /////////////////////////////////////////////////////////////////: private void traceDebug(String s){ if (debug) System.out.println("-->ivy<-- "+s); } - - + // TODO find out if this is useful or not ... + private void classes( String msg_classes[] ) { + messages_classes = msg_classes; + } -} // class Ivy +} |