aboutsummaryrefslogtreecommitdiff
path: root/src/Ivy.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/Ivy.java')
-rwxr-xr-xsrc/Ivy.java358
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();