diff options
author | jestin | 2004-01-12 09:48:49 +0000 |
---|---|---|
committer | jestin | 2004-01-12 09:48:49 +0000 |
commit | 64c95fd653177361f7e691ab345ed54d42ef6aed (patch) | |
tree | db912d9c0a42a9010c9841a2a799fde0a3258456 /src/Ivy.java | |
parent | b06dd52e8550e16cf877a076d24eda2c91e78d7c (diff) | |
download | ivy-java-64c95fd653177361f7e691ab345ed54d42ef6aed.zip ivy-java-64c95fd653177361f7e691ab345ed54d42ef6aed.tar.gz ivy-java-64c95fd653177361f7e691ab345ed54d42ef6aed.tar.bz2 ivy-java-64c95fd653177361f7e691ab345ed54d42ef6aed.tar.xz |
1.2.5 pre
Diffstat (limited to 'src/Ivy.java')
-rwxr-xr-x | src/Ivy.java | 358 |
1 files changed, 268 insertions, 90 deletions
diff --git a/src/Ivy.java b/src/Ivy.java index b11d546..e3cc0bb 100755 --- a/src/Ivy.java +++ b/src/Ivy.java @@ -7,10 +7,28 @@ *<pre> *Ivy bus = new Ivy("Dummy agent","ready",null); *bus.bindMsg("(.*)",myMessageListener); - *bus.start(null); + *bus.start(getDomain(null)); *</pre> * * CHANGELOG: + * 1.2.4: + * - added an accessor for doSendToSelf + * - waitForMsg() and waitForClient() to make the synchronization with + * other Ivy agents easier + * - with the bindAsyncMsg() to subscribe and perform each callback in a + * new Thread + * - bindMsg(regexp,messagelistener,boolean) allow to subscribe with a + * synchrone/asynch exectution + * - API change, IvyException raised when \n or \0x3 are present in bus.sendMsg() + * - bindListener are now handled + * - removeApplicationListener can throw IvyException + * - bus.start(null) now starts on getDomain(null), first the IVYBUS + * property, then the DEFAULT_DOMAIN, 127:2010 + * - bindMsg() now throws an IvyException if the regexp is invalid !!! + * BEWARE, this can impact lots of programs ! (fixes J007) + * - no more includes the "broadcasting on " in the domain(String) method + * - new function sendToSelf(boolean) allow us to send messages to + * ourselves * 1.2.3: * - adds a IVYBUS property to propagate the domain once set. This way, * children forked through Ivy java can inherit from the current value. @@ -19,7 +37,7 @@ * added the String domains(String d) function, in order to display the * domain list * 1.2.1: - * bus.start(null) now starts on DEFAULT_DOMAIN + * bus.start(null) now starts on DEFAULT_DOMAIN. ( correction 1.2.4 This was not true.) * added the getDomains in order to correctly display the domain list * checks if the serverThread exists before interrupting it * no has unBindMsg(String) @@ -44,11 +62,12 @@ public class Ivy implements Runnable { /** * the name of the application on the bus */ - String appName; + protected String appName; /** * the protocol version number */ - public static final int PROCOCOLVERSION = 3 ; + public static final int PROTOCOLVERSION = 3 ; + public static final int PROTOCOLMINIMUM = 3 ; /** * the port for the UDP rendez vous, if none is supplied */ @@ -61,24 +80,23 @@ public class Ivy implements Runnable { * the library version, useful for development purposes only, when java is * invoked with -DIVY_DEBUG */ - public static final String libVersion ="1.2.3"; + public static final String libVersion ="1.2.4"; private boolean debug; - private static int serial=0; /* an unique ID for each regexp */ private static int clientSerial=0; /* an unique ID for each IvyClient */ private ServerSocket app; private Vector watchers; private volatile Thread serverThread; // to ensure quick communication of the end - private Hashtable callbacks = new Hashtable(); private Hashtable clients = new Hashtable(); private Vector ivyApplicationListenerList = new Vector(); - private String messages_classes[] = null; - private boolean sendToSelf = false ; + private Vector ivyBindListenerList = new Vector(); + private Vector sendThreads = new Vector(); private boolean stopped = false; - int applicationPort; /* Application port number */ - Hashtable regexp_out = new Hashtable(); - String ready_message = null; - + protected int applicationPort; /* Application port number */ + protected String ready_message = null; + protected boolean doProtectNewlines = false ; + private boolean doSendToSelf = false ; + protected SelfIvyClient selfIvyClient ; public final static int TIMEOUTLENGTH = 3000; /** @@ -96,6 +114,49 @@ public class Ivy implements Runnable { ready_message = message; debug = (System.getProperty("IVY_DEBUG")!=null); if ( appcb != null ) ivyApplicationListenerList.addElement( appcb ); + selfIvyClient=new SelfIvyClient(this,name); + } + + /** + * Waits for a message to be received + * + * @since 1.2.4 + * @param regexp the message we're waiting for to continue the main thread. + * @param timeout in millisecond, 0 if infinite + * @return the IvyClient who sent the message, or null if the timeout is + * reached + */ + public IvyClient waitForMsg(String regexp,int timeout) throws IvyException { + Waiter w = new Waiter(timeout); + int re = bindMsg(regexp,w); + IvyClient ic=w.waitFor(); + unBindMsg(re); + return ic; + } + + /** + * Waits for an other IvyClient to join the bus + * + * @since 1.2.4 + * @param name the name of the client we're waiting for to continue the main thread. + * @param timeout in millisecond, 0 if infinite + * @return the first IvyClient with the name or null if the timeout is + * reached + */ + public IvyClient waitForClient(String name,int timeout) throws IvyException { + IvyClient ic; + if (name==null) throw new IvyException("null name given to waitForClient"); + // first check if client with the same name is on the bus + for (Enumeration e=clients.elements();e.hasMoreElements();) { + ic = (IvyClient)e.nextElement(); + if (name.compareTo(ic.getApplicationName())==0) return ic; + } + // if not enter the waiting loop + WaiterClient w = new WaiterClient(name,timeout); + int i = addApplicationListener(w); + ic=w.waitForClient(); + removeApplicationListener(i); + return ic; } /** @@ -113,7 +174,7 @@ public class Ivy implements Runnable { * */ public void start(String domainbus) throws IvyException { - if (domainbus==null) domainbus=DEFAULT_DOMAIN; + if (domainbus==null) domainbus=getDomain(null); Properties sysProp = System.getProperties(); sysProp.put("IVYBUS",domainbus); try { @@ -123,7 +184,7 @@ public class Ivy implements Runnable { } catch (IOException e) { throw new IvyException("can't open TCP service socket " + e ); } - traceDebug("lib: "+libVersion+" protocol: "+PROCOCOLVERSION+" TCP service open on port "+applicationPort); + traceDebug("lib: "+libVersion+" protocol: "+PROTOCOLVERSION+" TCP service open on port "+applicationPort); watchers = new Vector(); Domain[] d = parseDomains(domainbus); @@ -144,7 +205,11 @@ public class Ivy implements Runnable { int index=0; while ( st.hasMoreTokens()) { String s = st.nextToken() ; - d[index++]=new Domain(IvyWatcher.getDomain(s),IvyWatcher.getPort(s)); + try { + d[index++]=new Domain(IvyWatcher.getDomain(s),IvyWatcher.getPort(s)); + } catch (IvyException ie) { + // do nothing + } } return d; } @@ -177,31 +242,83 @@ public class Ivy implements Runnable { } /** - * Toggles the sending of messages to oneself + * Toggles the sending of messages to oneself, the remote client's + * IvyMessageListeners are processed first, and ourself afterwards. + * @param boolean true if you want to send the message to yourself. Default + * is false + * @since 1.2.4 + */ + public void sendToSelf(boolean b) {doSendToSelf=b;} + + /** + * @param boolean do I send message to myself ? + * @since 1.2.4 + */ + public boolean isSendToSelf() {return doSendToSelf;} + + /** + * returns our self IvyClient. + * @since 1.2.4 + */ + public IvyClient getSelfIvyClient() {return selfIvyClient;} + + /** + * Toggles the encoding/decoding of messages to prevent bugs related to the + * presence of a "\n" + * @param boolean true if you want to send the message to yourself. Default + * is false. + * @since 1.2.5? + * The default escape character is a ESC 0x1A + */ + private void protectNewlines(boolean b) {doProtectNewlines=b;} + + /** + * Performs a pattern matching according to everyone's regexps, and sends + * the results to the relevant ivy agents sequentially + * + * @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) throws IvyException { + return sendMsg(message,false); + } + + /** + * Performs a pattern matching according to everyone's regexps, and sends + * the results to the relevant ivy agents, using as many threads as needed. * + * @since 1.2.4 + * @param message A String which will be compared to the regular + * expressions of the different clients + * @return always returns -1 */ - public void sendToSelf(boolean b) {sendToSelf=b;} + public int sendAsyncMsg(String message,boolean async) throws IvyException { + return sendMsg(message,true); + } /** * 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> + * + * @since 1.2.4 * @param message A String which will be compared to the regular * expressions of the different clients - * @return the number of messages actually sent + * @param async if true, the sending will be performed in a separate thread, + * default is false + * @return if async is true, always returns -1, else returns the number of messages actually sent */ - public int sendMsg( String message ) { + public int sendMsg(String message,boolean async) throws IvyException { int count = 0; - // an alternate implementation would one sender thread per client - // instead of one for all the clients. It might be a performance issue + if (doProtectNewlines) + message=IvyClient.encode(message); + else if ( (message.indexOf(IvyClient.newLineChar)!=-1)||(message.indexOf(IvyClient.endArgChar)!=-1)) + throw new IvyException("newline character not allowed in Ivy messages"); for ( Enumeration e=clients.elements();e.hasMoreElements();) { IvyClient client = (IvyClient)e.nextElement(); - count += client.sendMsg( message ); - } - if (sendToSelf) { - // TODO + count += client.sendMsg(message, async); } + if (doSendToSelf) count+=selfIvyClient.sendSelfMsg(message); return count; } @@ -221,17 +338,57 @@ public class Ivy implements Runnable { * interface, on the AWT/Swing framework * @return the id of the regular expression */ - public int bindMsg(String regexp, IvyMessageListener callback ) { - // creates a new binding (regexp,callback) - Integer key = new Integer(serial++); - regexp_out.put(key,regexp); - callbacks.put(key,callback ); + public int bindMsg(String sregexp, IvyMessageListener callback ) throws IvyException { + return bindMsg(sregexp,callback,false); + } + + /** + * Subscribes to a regular expression with asyncrhonous callback execution. + * + * Same as bindMsg, except that the callback will be executed in a separate + * thread each time. + * WARNING : there is no way to predict the order of execution + * of the * callbacks, i.e. a message received might trigger a callback before + * another one sent before + * + * @since 1.2.4 + * @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 int ID of the regular expression. + */ + public int bindAsyncMsg(String sregexp, IvyMessageListener callback ) throws IvyException { + return bindMsg(sregexp,callback,true); + } + + /** + * 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" + * @since 1.2.4 + * @param regexp a perl regular expression, groups are done with parenthesis + * @param callback any objects implementing the IvyMessageListener + * interface, on the AWT/Swing framework + * @param async if true, each callback will be run in a separate thread, + * default is false + * @return the id of the regular expression + */ + public int bindMsg(String sregexp, IvyMessageListener callback,boolean async ) throws IvyException { + // adds the regexp to our collection in selfIvyClient + int key=selfIvyClient.bindMsg(sregexp,callback,async); // notifies the other clients this new regexp for (Enumeration e=clients.elements();e.hasMoreElements();) { IvyClient c = (IvyClient)e.nextElement(); - c.sendRegexp(key.intValue(),regexp); + c.sendRegexp(key,sregexp); } - return key.intValue(); + return key; } /** @@ -240,14 +397,9 @@ public class Ivy implements Runnable { * @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 ) ) { - throw new IvyException("client wants to remove an unexistant regexp "+id); - } - for (Enumeration e=clients.elements();e.hasMoreElements();) { + selfIvyClient.unBindMsg(id); + for (Enumeration e=clients.elements();e.hasMoreElements();) ((IvyClient)e.nextElement()).delRegexp(id ); - } } /** @@ -258,18 +410,31 @@ public class Ivy implements Runnable { * @param String the string for the regular expression */ public boolean unBindMsg(String re) { - for (Enumeration e=regexp_out.keys();e.hasMoreElements();) { - Integer k = (Integer)e.nextElement(); - if ( ((String)regexp_out.get(k)).compareTo(re) == 0) { - try { - unBindMsg(k.intValue()); - } catch (IvyException ie) { - return false; - } - return true; - } + return selfIvyClient.unBindMsg(re); + } + + /** + * adds a bind listener to a bus + * @param callback is an object implementing the IvyBindListener interface + * @return the id of the bind listener, useful if you wish to remove it later + * @since 1.2.4 + */ + public int addBindListener(IvyBindListener callback){ + ivyBindListenerList.addElement(callback); + return ivyBindListenerList.indexOf(callback); + } + + /** + * removes a bind listener + * @param id the id of the bind listener to remove + * @since 1.2.4 + */ + public void removeBindListener(int id) throws IvyException { + try { + ivyBindListenerList.removeElementAt(id); + } catch (ArrayIndexOutOfBoundsException aie) { + throw new IvyException(id+" is not a valid Id"); } - return false; } /** @@ -281,32 +446,49 @@ public class Ivy implements Runnable { */ public int addApplicationListener(IvyApplicationListener callback){ ivyApplicationListenerList.addElement(callback); - int id = ivyApplicationListenerList.indexOf( callback ); - return id; + return ivyApplicationListenerList.indexOf( callback ); } /** * removes an application listener * @param id the id of the application listener to remove */ - public void removeApplicationListener(int id){ - ivyApplicationListenerList.removeElementAt(id); + public void removeApplicationListener(int id) throws IvyException { + try { + ivyApplicationListenerList.removeElementAt(id); + } catch (ArrayIndexOutOfBoundsException aie) { + throw new IvyException(id+" is not a valid Id"); + } } /* invokes the application listeners upon arrival of a new Ivy client */ - public void connect(IvyClient client){ + protected void clientConnects(IvyClient client){ for ( int i = 0 ; i < ivyApplicationListenerList.size(); i++ ) { ((IvyApplicationListener)ivyApplicationListenerList.elementAt(i)).connect(client); } } - /* invokes the application listeners upon arrival of a new Ivy client */ - void disconnectReceived(IvyClient client){ + /* invokes the application listeners upon the departure of an Ivy client */ + protected void clientDisconnects(IvyClient client){ for ( int i = 0 ; i < ivyApplicationListenerList.size(); i++ ) { ((IvyApplicationListener)ivyApplicationListenerList.elementAt(i)).disconnect(client); } } + /* invokes the bind listeners */ + protected void regexpReceived(IvyClient client,int id,String sregexp){ + for ( int i = 0 ; i < ivyBindListenerList.size(); i++ ) { + ((IvyBindListener)ivyBindListenerList.elementAt(i)).bindPerformed(client,id,sregexp); + } + } + + /* invokes the bind listeners */ + protected void regexpDeleted(IvyClient client,int id,String sregexp){ + for ( int i = 0 ; i < ivyBindListenerList.size(); i++ ) { + ((IvyBindListener)ivyBindListenerList.elementAt(i)).unbindPerformed(client,id,sregexp); + } + } + /* * removes a client from the list */ @@ -316,9 +498,9 @@ public class Ivy implements Runnable { * invokes the application listeners when we are summoned to die * then stops */ - public void dieReceived(IvyClient client, int id){ + public void dieReceived(IvyClient client, int id,String message){ for ( int i=0 ;i<ivyApplicationListenerList.size();i++ ) { - ((IvyApplicationListener)ivyApplicationListenerList.elementAt(i)).die(client,id); + ((IvyApplicationListener)ivyApplicationListenerList.elementAt(i)).die(client,id,message); } } @@ -360,21 +542,13 @@ public class Ivy implements Runnable { // /////////////////////////////////////////////////////////////////: - synchronized void addClient(Socket socket,boolean peerCalling) throws IOException { + synchronized void addClient(Socket socket,boolean peerCalling,int protocolVersion) throws IOException { if (stopped) return; - IvyClient client = new IvyClient( this, socket,peerCalling,new Integer(clientSerial++)); + IvyClient client = new IvyClient( this, socket,peerCalling,new Integer(clientSerial++),protocolVersion); clients.put(client.getClientKey(),client); traceDebug(getClientNames()); } - void callCallback(IvyClient client, Integer key, String[] tab) throws IvyException { - IvyMessageListener callback=(IvyMessageListener)callbacks.get(key); - if (callback==null){ - throw new IvyException("(callCallback) Not regexp matching id "+key.intValue()); - } - callback.receive( client, tab); - } - private static String[] myTokenize(String s,String separator) { int index=0, last=0, length=s.length(); Vector v = new Vector(); @@ -402,20 +576,6 @@ public class Ivy implements Runnable { } - /** - * checks the "validity" of a regular expression. - */ - boolean CheckRegexp( String exp ) { - boolean regexp_ok = true; - if ( exp.startsWith( "^" )&&messages_classes!=null) { - regexp_ok=false; - for (int i=0 ; i < messages_classes.length;i++) { - if (messages_classes[i].equals(exp.substring(1))) return true; - } - } - return regexp_ok; - } - /* * prevents two clients from connecting to each other at the same time * there might still be a lingering bug here, that we could avoid with the @@ -433,14 +593,14 @@ public class Ivy implements Runnable { /* * the service socket thread reader main loop */ - public void run() { + public void run() { // System.out.println("Ivy service Thread started"); // THREADDEBUG Thread thisThread=Thread.currentThread(); while(thisThread==serverThread){ try { Socket socket = app.accept(); if ((thisThread!=serverThread)||stopped) break; // early disconnexion - addClient(socket,true); // the peer called me + addClient(socket,true,0); // the peer called me TODO I don't know his protocol version } catch (InterruptedIOException ie) { if (thisThread!=serverThread) break; } catch( IOException e ) { @@ -459,6 +619,21 @@ public class Ivy implements Runnable { private void traceDebug(String s){ if (debug) System.out.println("-->ivy<-- "+s); } + synchronized void registerThread(Thread t) { sendThreads.add(t); } + synchronized void unRegisterThread(Thread t) { sendThreads.remove(t); } + synchronized Thread getOneThread() { + if (sendThreads.size()==0) return null; + return (Thread) sendThreads.firstElement(); + } + + void waitForAll() { + Thread t; + System.out.println("DEVELOPMENT WAITFORALL sendThreads size : " + sendThreads.size()); + try { while ((t=getOneThread())!=null) { t.join(); } } + catch (InterruptedException ie) { System.out.println("waitForAll Interrupted"); } + System.out.println("DEVELOPMENT END WAITFORALL"); + } + /* a small private method for debbugging purposes */ private String getClientNames() { String s = appName+" clients are: "; @@ -469,7 +644,7 @@ public class Ivy implements Runnable { } public String domains(String toparse) { - String s="broadcasting on "; + String s=""; Ivy.Domain[] d = parseDomains(toparse); for (int index=0;index<d.length;index++) { s+=d[index].getDomainaddr()+":"+d[index].getPort()+" "; @@ -495,7 +670,10 @@ public class Ivy implements Runnable { Ivy bus = new Ivy("Test Unitaire","TU ready",null); try { bus.start(null); - try { Thread.sleep(2000); } catch (InterruptedException ie) { } + System.out.println("waiting 5 seconds for a coucou"); + System.out.println(((bus.waitForMsg("^coucou",5000))!=null)?"coucou received":"coucou not received"); + System.out.println("waiting 5 seconds for IvyProbe"); + System.out.println(((bus.waitForClient("IVYPROBE",5000))!=null)?"Ivyprobe joined the bus":"nobody came"); bus.stop(); } catch (IvyException ie) { ie.printStackTrace(); |