diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/After.java | 10 | ||||
-rw-r--r-- | src/Ghost.java | 1 | ||||
-rwxr-xr-x | src/Ivy.java | 811 | ||||
-rwxr-xr-x | src/IvyApplicationAdapter.java | 2 | ||||
-rwxr-xr-x | src/IvyApplicationListener.java | 2 | ||||
-rwxr-xr-x | src/IvyClient.java | 178 | ||||
-rw-r--r-- | src/IvyDaemon.java | 23 | ||||
-rwxr-xr-x | src/IvyException.java | 2 | ||||
-rw-r--r-- | src/IvyHttpGatewayClient.java | 1 | ||||
-rw-r--r-- | src/IvyHttpGatewayServlet.java | 24 | ||||
-rw-r--r-- | src/IvyHttpGatewayTest.java | 1 | ||||
-rwxr-xr-x | src/IvyMessageListener.java | 2 | ||||
-rwxr-xr-x | src/IvyWatcher.java | 113 | ||||
-rw-r--r-- | src/Probe.java | 125 | ||||
-rw-r--r-- | src/ProxyClient.java | 96 | ||||
-rw-r--r-- | src/ProxyMaster.java | 73 | ||||
-rw-r--r-- | src/Puppet.java | 52 | ||||
-rw-r--r-- | src/SelfIvyClient.java | 113 | ||||
-rw-r--r-- | src/WaiterClient.java | 4 |
19 files changed, 908 insertions, 725 deletions
diff --git a/src/After.java b/src/After.java index b36d5de..0ca78ca 100644 --- a/src/After.java +++ b/src/After.java @@ -7,6 +7,9 @@ * (c) CENA * * Changelog: + * 1.2.14 + * - gracefully quits when message is received, by quitting the Ivy + * threads with bus.stop() , instead of invoking system.exit * 1.2.8: new in the ivy package */ package fr.dgac.ivy.tools ; @@ -29,9 +32,9 @@ public class After extends IvyApplicationAdapter implements IvyMessageListener { case 'b': domain=opt.getOptarg(); break; case 't': timeout=Integer.parseInt(opt.getOptarg()); break; case 'h': - default: System.out.println(helpmsg); System.exit(0); + default: System.out.println(helpmsg); return; } - if (opt.getOptind()!=args.length-1) { System.out.println(helpmsg); System.exit(0); } + if (opt.getOptind()!=args.length-1) { System.out.println(helpmsg); return; } String regexp=args[opt.getOptind()]; Ivy bus=new Ivy(name,name+" ready",null); bus.bindMsgOnce(regexp,new After(bus)); @@ -41,7 +44,6 @@ public class After extends IvyApplicationAdapter implements IvyMessageListener { try { Thread.sleep(timeout*1000); } catch (InterruptedException ie) { } System.out.println(regexp+" not received, bailing out"); bus.stop(); - System.exit(-1); } else { System.out.println("waiting forever for "+regexp); } @@ -56,12 +58,10 @@ public class After extends IvyApplicationAdapter implements IvyMessageListener { public void die( IvyClient client, int id, String msgarg) { System.out.println("die received, bailing out"); bus.stop(); - System.exit(-1); } public void receive(IvyClient ic,String[] args) { bus.stop(); - System.exit(0); } } diff --git a/src/Ghost.java b/src/Ghost.java index f8f76cf..b96de8a 100644 --- a/src/Ghost.java +++ b/src/Ghost.java @@ -11,7 +11,6 @@ package fr.dgac.ivy ; import java.io.*; import java.net.*; import java.util.*; -import org.apache.regexp.*; class Ghost extends IvyClient { diff --git a/src/Ivy.java b/src/Ivy.java index 5e3362f..b147af3 100755 --- a/src/Ivy.java +++ b/src/Ivy.java @@ -1,10 +1,12 @@ /** * a software bus package * - * @author Yannick Jestin - * @author <a href="http://www.tls.cena.fr/products/ivy/">http://www.tls.cena.fr/products/ivy/</a> + * @author Yannick Jestin <a + * href="mailto:yannick.jestin@enac.fr">yannick.jestin&enac.fr</a> + * @author <a href="http://www2.tls.cena.fr/products/ivy/">http://www.tls.cena.fr/products/ivy/</a> * * (c) CENA 1998-2004 + * (c) ENAC 2005-2011 * *<pre> *Ivy bus = new Ivy("Dummy agent","ready",null); @@ -13,6 +15,37 @@ *</pre> * * CHANGELOG: + * 1.2.14 + * - added a lock mechanism to be sure that once a connexion has been + * initiated, the ready message will be sent before stopping the bus + * now: Ivy b = new Ivy(...); b.sendMsg("coucou"); b.stop(); should + * send messages (at least the ready message) if there is a connexion + * attempt made before b.stop() is effective. To be sure, there is a 200ms + * delay before b.stop() can be effective (the Threads stopped, the sockets + * closed) + * - reintroduced a mechanism to allow the drop of a double connexion + * attempt + * - removed protected methods from javadoc + * - switch to apache fop + docbook for documentation + * - added sypport to the Swing Dispatch Thread in the bindAsyncMsg api + * (this breaks the former API, this is BAAAAAD). Use BindType.SWING as the + * latter argument + * - javadoc updated + * - appName gone private, with a protected accessor + * - add a lockApp synchronization for application socket control + * - use of stringbuffers to concatenate strings, instead of using +, which + * could lead to a quadractic cost in the number of iteraction (the growing + * string was recopied in each iteration) + * - throws RuntimeException instead of System.exit(), allows code reuse + * - ready message is set to appName + " READY" if null has been provided + * - switch from gnu regexp (deprecated) to the built in java regexp + * - when possible, move the regexp Pattern.compile in static areas, to avoid multiple + * calls + * - add generic types to declarations + * - fxed a potential null pointer dereference on quit + * - lowercase CheckRegexp to checkRegexp (bad practice, thanks to FindBugs) + * - recopy the filter String[] in setfilter, to avoid exposing internal + * representation (unsafe operation) * 1.2.13: * - adds support for RESyntaxException * 1.2.12: @@ -86,114 +119,131 @@ * adding the sendToSelf feature * fixed the printStackTrace upon closing of the ServerSocket after a close() */ -package fr.dgac.ivy ; +package fr.dgac.ivy; import java.net.*; import java.io.*; import java.util.*; import gnu.getopt.Getopt; -import org.apache.regexp.*; +import java.util.regex.*; public class Ivy implements Runnable { /** - * the name of the application on the bus + * the protocol version number. */ - String appName; + public static final int PROTOCOLVERSION = 3; + public static final int PROTOCOLMINIMUM = 3; + private static final int GRACEDELAY = 200; // in milliseconds /** - * the protocol version number + * the port for the UDP rendez vous, if none is supplied. */ - public static final int PROTOCOLVERSION = 3 ; - public static final int PROTOCOLMINIMUM = 3 ; + public static final int DEFAULT_PORT = 2010; /** - * the port for the UDP rendez vous, if none is supplied + * the domain for the UDP rendez vous. */ - 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 static final String DEFAULT_DOMAIN = "127.255.255.255:" + DEFAULT_PORT; /** * the library version, useful for development purposes only, when java is * invoked with -DIVY_DEBUG */ - public static final String libVersion ="1.2.13"; + public static final String LIBVERSION ="1.2.14"; + public static final int TIMEOUTLENGTH = 1000; + private String appName; + private int applicationPort; /* Application port number */ + private String ready_message = null; + private boolean doProtectNewlines = false; + private SelfIvyClient selfIvyClient; + private Object lockApp = new Object(); private boolean debug; private ServerSocket app; - private Vector watchers = new Vector(); + private Vector<IvyWatcher> watchers = new Vector<IvyWatcher>(); private volatile Thread serverThread; // to ensure quick communication of the end - private Hashtable clients = new Hashtable(); - private Hashtable half = new Hashtable(); - private Vector ivyApplicationListenerList = new Vector(); - private Vector ivyBindListenerList = new Vector(); - private Vector sendThreads = new Vector(); + private Hashtable<Integer,IvyClient> clients = new Hashtable<Integer,IvyClient>(); + private Hashtable<Integer,IvyClient> half = new Hashtable<Integer,IvyClient>(); + private Vector<IvyApplicationListener> ivyApplicationListenerList = new Vector<IvyApplicationListener>(); + private Vector<IvyBindListener> ivyBindListenerList = new Vector<IvyBindListener>(); + private Vector<Thread> sendThreads = new Vector<Thread>(); private String[] filter = null; - private boolean stopped=true; - 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 = 1000; - private static int serial=0; - private int myserial=serial++; - static long current = System.currentTimeMillis(); - private static java.util.Random generator = new java.util.Random(current*(serial+1)); - private String watcherId=null; - + private boolean stopped = true; + private boolean starting = false; + protected Object readyToSend = new Object(); + private boolean doSendToSelf = false; + private static int serial = 0; + private int myserial = serial++; + private static long current = System.currentTimeMillis(); + private static java.util.Random generator = new java.util.Random(current*(serial + 1)); + private String watcherId = null; + private static Pattern rangeRE; // tcp range min and max + private static Pattern bounded; + + private static final Object lock = new Object(); + /** * 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 message The hellow message you will send once ready. It can be + * null, in which case "appname READY" will be the default * @param appcb A callback handling the notification of connexions and - * disconnections, may be null + * disconnections, (may be null for most agents) */ - public Ivy(String name, String message, IvyApplicationListener appcb) { + public Ivy(final String name, final String message, final IvyApplicationListener appcb) { appName = name; - ready_message = message; - debug = (System.getProperty("IVY_DEBUG")!=null); - if ( appcb != null ) ivyApplicationListenerList.addElement( appcb ); - selfIvyClient=new SelfIvyClient(this,name); + ready_message = (message == null) ? name + " READY" : message; + debug = + (System.getProperty("IVY_DEBUG") != null) + || (System.getProperty("IVY_DEBUG") != null); + if ( appcb != null ) { + ivyApplicationListenerList.addElement( appcb ); + } + selfIvyClient = new SelfIvyClient(this , name); } /** - * Waits for a message to be received + * 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 + * @throws IvyException if something bad happens * @return the IvyClient who sent the message, or null if the timeout is * reached */ - public IvyClient waitForMsg(String regexp,int timeout) throws IvyException { + public final IvyClient waitForMsg(final String regexp , final int timeout) throws IvyException { Waiter w = new Waiter(timeout); - int re = bindMsg(regexp,w); - IvyClient ic=w.waitFor(); + int re = bindMsg(regexp , w); + IvyClient ic = w.waitFor(); unBindMsg(re); return ic; } /** - * Waits for an other IvyClient to join the bus + * 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 + * @throws IvyException if something bad happens * @return the first IvyClient with the name or null if the timeout is * reached */ - public IvyClient waitForClient(String name,int timeout) throws IvyException { + public final IvyClient waitForClient(final String name , final int timeout) throws IvyException { IvyClient ic; - if (name==null) throw new IvyException("null name given to waitForClient"); + if (name == null) { + throw new IvyException("null name given to waitForClient"); + } // first check if client with the same name is on the bus - if ((ic=alreadyThere(clients,name))!=null) return ic; + ic = alreadyThere(clients , name); + if (ic != null) { + return ic; + } // if not enter the waiting loop - WaiterClient w = new WaiterClient(name,timeout,clients); + WaiterClient w = new WaiterClient(name , timeout , clients); int i = addApplicationListener(w); - ic=w.waitForClient(); + ic = w.waitForClient(); removeApplicationListener(i); return ic; } @@ -201,15 +251,9 @@ public class Ivy implements Runnable { /* * since 1.2.8 */ - static protected IvyClient alreadyThere(Hashtable c,String name) { - IvyClient ic; - for (Enumeration e=c.elements();e.hasMoreElements();) { - try { - ic = (IvyClient)e.nextElement(); - } catch (ArrayIndexOutOfBoundsException _ ) { - return null; // with gij, it ... can happen - } - if ((ic!=null)&&(name.compareTo(ic.getApplicationName())==0)) return ic; + protected static IvyClient alreadyThere(final Hashtable<Integer , IvyClient> c , final String name) { + for (IvyClient ic : c.values()) { + if ((ic != null)&&(name.compareTo(ic.getApplicationName()) == 0)) return ic; } return null; } @@ -220,8 +264,11 @@ public class Ivy implements Runnable { * <li>One thread (IvyWatcher) for each traffic rendezvous (either UDP broadcast or TCPMulticast) * <li>One thread (serverThread/Ivy) to accept incoming connexions on server socket * <li>a thread for each IvyClient when the connexion has been done - * - * @param domainbus a domain of the form 10.0.0:1234, it is similar to the + * @throws IvyException if there is a problem joining the bus + * @param domainbus a domain of the form 10.0.0:1234, A good practice is to + * sick to a null value, so that your agent will honor the IVY_BUS parameter + * given to the jvm (java -DIVYBUS= ... . Otherwise, you can provide some + * hard-coded value, 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 @@ -230,108 +277,124 @@ public class Ivy implements Runnable { * 1.2.8: goes synchronized. I don't know if it's really useful * */ - public synchronized void start(String domainbus) throws IvyException { + + public final void start(final String domainbus) throws IvyException { if (!stopped) throw new IvyException("cannot start a bus that's already started"); + setStarting(true); // this will remain true entil one of the PacketSenders has finished stopped=false; - if (domainbus==null) domainbus=getDomain(null); - Properties sysProp = System.getProperties(); - sysProp.put("IVYBUS",domainbus); - String range=(String)sysProp.get("IVYRANGE"); - RE rangeRE; // tcp range min and max - try { - rangeRE = new RE("(\\d+)-(\\d+)"); // tcp range min and max - } catch ( RESyntaxException res ) { - throw new IvyException("Regular Expression bug in Ivy source code ... bailing out"); + String db = domainbus; + if (db == null) { + db = getDomain(null); } - if ((range!=null)&&rangeRE.match(range)) { - int rangeMin=Integer.parseInt(rangeRE.getParen(1)); - int rangeMax=Integer.parseInt(rangeRE.getParen(2)); - int index=rangeMin; - traceDebug("trying to allocate a TCP port between "+rangeMin+" and "+rangeMax); - boolean allocated=false; + Properties sysProp = System.getProperties(); + sysProp.put("IVYBUS" , db); + String range = (String)sysProp.get("IVYRANGE"); + Matcher match; + if ((range != null)&&(match = rangeRE.matcher(range)).matches()) { + int rangeMin = Integer.parseInt(match.group(1)); + int rangeMax = Integer.parseInt(match.group(2)); + int index = rangeMin; + traceDebug("trying to allocate a TCP port between " + rangeMin + " and " + rangeMax); + boolean allocated = false; while (!allocated) try { - if (index>rangeMax) throw new IvyException("no available port in IVYRANGE" + range ); - app = new ServerSocket(index); - app.setSoTimeout(TIMEOUTLENGTH); - applicationPort = app.getLocalPort(); - allocated=true; + if (index>rangeMax) throw new IvyException("no available port in IVYRANGE" + range ); + synchronized (lockApp) { + app = new ServerSocket(index); + app.setSoTimeout(TIMEOUTLENGTH); + applicationPort = app.getLocalPort(); + } + allocated = true; } catch (BindException e) { index++; } catch (IOException e) { - throw new IvyException("can't open TCP service socket " + e ); + throw new IvyException("can't open TCP service socket " + e ); } } else try { - app = new ServerSocket(0); - app.setSoTimeout(TIMEOUTLENGTH); - applicationPort = app.getLocalPort(); + synchronized (lockApp) { + app = new ServerSocket(0); + app.setSoTimeout(TIMEOUTLENGTH); + applicationPort = app.getLocalPort(); + } } catch (IOException e) { throw new IvyException("can't open TCP service socket " + e ); } - // app.getInetAddress().getHostName()) is always 0.0.0.0 - traceDebug("lib: "+libVersion+" protocol: "+PROTOCOLVERSION+" TCP service open on port "+applicationPort); + traceDebug("lib: " + LIBVERSION + " protocol: " + PROTOCOLVERSION + " TCP service open on port " + applicationPort); - Domain[] d = parseDomains(domainbus); - if (d.length==0) throw new IvyException("no domain found in "+domainbus); - watcherId=getWBUId().replace(' ','*'); // no space in the watcherId + Domain[] d = parseDomains(db); + if (d.length == 0) throw new IvyException("no domain found in " + db); + watcherId = getWBUId().replace(' ' , '*'); // no space in the watcherId // readies the rendezvous : an IvyWatcher (thread) per domain bus - for (int index=0;index<d.length;index++) - watchers.addElement(new IvyWatcher(this,d[index].domainaddr,d[index].port)); + for (Domain dom: d) watchers.addElement(new IvyWatcher(this , dom.domainaddr , dom.port)); serverThread = new Thread(this); serverThread.start(); // sends the broadcasts and listen to incoming connexions - for (int i=0;i<watchers.size();i++){ ((IvyWatcher)watchers.elementAt(i)).start(); } + for (IvyWatcher iw: watchers) iw.doStart(); } - public Domain[] parseDomains(String domainbus) { - StringTokenizer st = new StringTokenizer(domainbus,","); + protected final Domain[] parseDomains(final String domainbus) { + StringTokenizer st = new StringTokenizer(domainbus , ","); Domain[] d = new Domain[st.countTokens()]; - int index=0; + int index = 0; while ( st.hasMoreTokens()) { - String s = st.nextToken() ; + String s = st.nextToken(); try { - d[index++]=new Domain(IvyWatcher.getDomain(s),IvyWatcher.getPort(s)); + d[index++] = new Domain(IvyWatcher.getDomain(s) , IvyWatcher.getPort(s)); } catch (IvyException ie) { - // do nothing - ie.printStackTrace(); + // do nothing + ie.printStackTrace(); } } // fixes the port values ... int lastport = Ivy.DEFAULT_PORT; - for (index--;index>=0;index--) { - Domain dom=d[index]; - if (dom.port==0) dom.port=lastport; - lastport=dom.port; + for (index--; index >= 0; index--) { + Domain dom = d[index]; + if (dom.port == 0) dom.port = lastport; + lastport = dom.port; } return d; } + + private void waitForRemote(String s) { + try { + while (starting==true) { + Thread.sleep(GRACEDELAY); + traceDebug("I'm waiting before "+s+", a starting tread is in progress"); + } + } catch (InterruptedException ie) { + // should not happen, and it's not a problem anyway + } + } + /** - * disconnects from the Ivy bus + * disconnects from the Ivy bus. */ - public void stop() { + public final void stop() { + waitForRemote("stopping"); if (stopped) return; - stopped=true; + stopped = true; + serverThread = null; traceDebug("beginning stopping"); try { // stopping the serverThread - Thread t=serverThread; - serverThread=null; - if (t!=null) t.interrupt(); // The serverThread might be stopped even before having been created - // System.out.println("IZZZ joining "+t); - try { t.join(); } catch ( InterruptedException _ ) { } - // TODO BUG avec gcj+kaffe, le close() reste pendu et ne rend pas la main - app.close(); + Thread t = serverThread; + if (t != null) { + t.interrupt(); // The serverThread might be stopped even before having been created + // System.out.println("IZZZ joining " + t); + try { t.join(); } catch ( InterruptedException ie ) { + ie.printStackTrace(); + } + } + synchronized (lockApp) { app.close(); } // stopping the IvyWatchers - for (int i=0;i<watchers.size();i++){ ((IvyWatcher)watchers.elementAt(i)).stop(); } + for (IvyWatcher iw: watchers) iw.doStop(); watchers.clear(); // stopping the remaining IvyClients - for (Enumeration e=clients.elements();e.hasMoreElements();) { - try { - IvyClient c = (IvyClient)e.nextElement(); - if (c!=null) { c.close(true);removeClient(c); } - } catch (ArrayIndexOutOfBoundsException _ ) { - continue; + for (IvyClient c : clients.values()) { + if (c != null) { + c.close(true); + removeClient(c); } } } catch (IOException e) { @@ -347,53 +410,62 @@ public class Ivy implements Runnable { * is false * @since 1.2.4 */ - public void sendToSelf(boolean b) {doSendToSelf=b;} + public final void sendToSelf(final boolean b) { + doSendToSelf = b; + } /** * do I send messsages to myself ? + * @return a boolean * @since 1.2.4 */ - public boolean isSendToSelf() {return doSendToSelf;} + public final boolean isSendToSelf() { + return doSendToSelf; + } /** - * returns our self IvyClient. + * selfIvyClient accesssor. + * @return our selfIvyClient + * @since 1.2.4 * @since 1.2.4 */ - public IvyClient getSelfIvyClient() {return selfIvyClient;} + public final SelfIvyClient getSelfIvyClient() { + return selfIvyClient; + } /** * Toggles the encoding/decoding of messages to prevent bugs related to the - * presence of a "\n" + * presence of a "\n". * @param b true if you want to enforce encoding of newlines. Default * is false. Every receiver will have to decode newlines * @since 1.2.5 * The default escape character is a ESC 0x1A */ - public void protectNewlines(boolean b) {doProtectNewlines=b;} + public final void protectNewlines(final boolean b) { + doProtectNewlines = b; + } /** * Performs a pattern matching according to everyone's regexps, and sends * the results to the relevant ivy agents. - * + * @throws IvyException if there is a problem sending the message * @param message A String which will be compared to the regular * expressions of the different clients * @return returns the number of messages actually sent */ - public int sendMsg(String message) throws IvyException { + public final int sendMsg(final String message) throws IvyException { int count = 0; - 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();) { - try { - IvyClient client = (IvyClient)e.nextElement(); - if (client!=null) count += client.sendMsg(message); - } catch (ArrayIndexOutOfBoundsException _ ) { - continue; // gij problem - } + waitForRemote("sending"); + synchronized (lock) { + traceDebug("sending "+message); + String msg = message; + if (doProtectNewlines) msg = IvyClient.encode(message); + else if ( (msg.indexOf(IvyClient.newLineChar) != -1)||(msg.indexOf(IvyClient.endArgChar) != -1)) + throw new IvyException("newline character not allowed in Ivy messages"); + for ( IvyClient client : clients.values()) if (client != null) count += client.sendMsg(msg); + if (doSendToSelf) count+=selfIvyClient.sendSelfMsg(msg); + traceDebug("end sending "+message+" to "+count+" clients"); } - if (doSendToSelf) count+=selfIvyClient.sendSelfMsg(message); return count; } @@ -411,10 +483,12 @@ public class Ivy implements Runnable { * @param sregexp a perl regular expression, groups are done with parenthesis * @param callback any objects implementing the IvyMessageListener * interface, on the AWT/Swing framework + * @throws IvyException if there is a problem in the binding, be it regexp + * or network * @return the id of the regular expression */ - public int bindMsg(String sregexp, IvyMessageListener callback ) throws IvyException { - return bindMsg(sregexp,callback,false); + public final int bindMsg(final String sregexp , final IvyMessageListener callback ) throws IvyException { + return bindMsg(sregexp , callback , BindType.NORMAL); } /** @@ -430,10 +504,15 @@ public class Ivy implements Runnable { * @param sregexp a perl compatible regular expression, groups are done with parenthesis * @param callback any objects implementing the IvyMessageListener * interface, on the AWT/Swing framework + * @param type if set to NORMAL, it's a normal bind, if it's ASYNC, the + * callback will be created in a newly spawned Thread (Heavy ressources), if + * it's SWING, the callback will be deferred to the Swing Event Dispatch + * Tread + * @throws IvyException if there is a problem binding (network, regexp...) * @return the int ID of the regular expression. */ - public int bindAsyncMsg(String sregexp, IvyMessageListener callback ) throws IvyException { - return bindMsg(sregexp,callback,true); + public final int bindAsyncMsg(final String sregexp, final IvyMessageListener callback, BindType type ) throws IvyException { + return bindMsg(sregexp , callback , type); } /** @@ -452,28 +531,22 @@ public class Ivy implements Runnable { * @param sregexp 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 + * @param type if NORMAL (default) it's a normal bind, if ASYNC, each callback will be run in a separate thread, if SWING, the callback will be deferred to the Swing Event Dispatch Thread + * default is NORMAL + * @throws IvyException if there is a problem binding (regexp, network) * @return the id of the regular expression */ - public int bindMsg(String sregexp, IvyMessageListener callback,boolean async ) throws IvyException { + public final int bindMsg(final String sregexp , final IvyMessageListener callback , final BindType type ) throws IvyException { // adds the regexp to our collection in selfIvyClient - int key=selfIvyClient.bindMsg(sregexp,callback,async); + int key = selfIvyClient.bindMsg(sregexp , callback , type); // notifies the other clients this new regexp - for (Enumeration e=clients.elements();e.hasMoreElements();) { - try { - IvyClient c = (IvyClient)e.nextElement(); - if (c!=null) c.sendRegexp(key,sregexp); - } catch (ArrayIndexOutOfBoundsException _ ) { - continue; // gij problem - } - } + for (IvyClient c : clients.values() ) if (c != null) c.sendRegexp(key , sregexp); return key; } /** * Subscribes to a regular expression for one time only, useful for - * requests, in cunjunction with getWBUId() + * requests, in cunjunction with getWBUId(). * * The callback will be executed once and only once, and the agent will * unsubscribe @@ -481,250 +554,227 @@ public class Ivy implements Runnable { * @param sregexp a perl regular expression, groups are done with parenthesis * @param callback any objects implementing the IvyMessageListener * interface, on the AWT/Swing framework + * @throws IvyException if there is a problem during the binding * @return the id of the regular expression */ - public int bindMsgOnce(String sregexp, IvyMessageListener callback ) throws IvyException { + public final int bindMsgOnce(final String sregexp, final IvyMessageListener callback ) throws IvyException { Once once = new Once(callback); - int id = bindMsg(sregexp,once); + int id = bindMsg(sregexp , once); once.setRegexpId(id); return id; } /** - * unsubscribes a regular expression + * unsubscribes a regular expression using the id provided at bind time. * * @param id the id of the regular expression, returned when it was bound + * @throws IvyException if the id is not valid anymore */ - public void unBindMsg(int id) throws IvyException { + public final void unBindMsg(final int id) throws IvyException { selfIvyClient.unBindMsg(id); - for (Enumeration e=clients.elements();e.hasMoreElements();) { - try { - IvyClient ic=(IvyClient)e.nextElement(); - if (ic!=null) ic.delRegexp(id ); - } catch (ArrayIndexOutOfBoundsException _ ) { - continue; - } - - } + for (IvyClient ic : clients.values() ) if (ic != null) ic.delRegexp(id ); } /** - * unsubscribes a regular expression + * unsubscribes a regular expression based on its string. * * @return a boolean, true if the regexp existed, false otherwise or * whenever an exception occured during unbinding * @param re the string for the regular expression */ - public boolean unBindMsg(String re) { return selfIvyClient.unBindMsg(re); } + public final boolean unBindMsg(final String re) { return selfIvyClient.unBindMsg(re); } /** - * adds a bind listener to a bus + * 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){ + public final int addBindListener(final IvyBindListener callback){ ivyBindListenerList.addElement(callback); return ivyBindListenerList.indexOf(callback); } /** - * removes a bind listener + * removes a bind listener. * @param id the id of the bind listener to remove + * @throws IvyException if id is not known * @since 1.2.4 */ - public void removeBindListener(int id) throws IvyException { + public final void removeBindListener(final int id) throws IvyException { try { ivyBindListenerList.removeElementAt(id); } catch (ArrayIndexOutOfBoundsException aie) { - throw new IvyException(id+" is not a valid Id"); + throw new IvyException(id + " is not a valid Id"); } } /** - * adds an application listener to a bus + * 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){ + public synchronized final int addApplicationListener(final IvyApplicationListener callback){ ivyApplicationListenerList.addElement(callback); return ivyApplicationListenerList.indexOf( callback ); } /** - * removes an application listener + * removes an application listener. * @param id the id of the application listener to remove + * @throws IvyException if there is no such id */ - public void removeApplicationListener(int id) throws IvyException { + public synchronized final void removeApplicationListener(final int id) throws IvyException { try { ivyApplicationListenerList.removeElementAt(id); } catch (ArrayIndexOutOfBoundsException aie) { - throw new IvyException(id+" is not a valid Id"); + throw new IvyException(id + " is not a valid Id"); } } /** - * sets the filter expression - * @param filter the extensive list of strings beginning the messages + * sets the filter expression. + * @param f the extensive list of strings beginning the messages * @since 1.2.9 * * once this filter is set, when a client subscribes to a regexp of the * form "^dummystring...", there is a check against the filter list. If no * keyword is found to match, the binding is just ignored. */ - public void setFilter(String[] filter){ this.filter=filter; } - - /** - * checks the "validity" of a regular expression if a filter has been set - * @since 1.2.9 - * @param exp a string regular expression - * must be synchronized ( RE is not threadsafe ) - */ - private static RE bounded ; + public final synchronized void setFilter(final String[] f){ + filter = java.util.Arrays.copyOf(f , f.length); + } static { + // compiles the static regexps try { - bounded = new RE("^\\^([a-zA-Z0-9_-]+).*"); - } catch ( RESyntaxException res ) { + rangeRE = Pattern.compile("(\\d+)-(\\d+)"); // tcp range min and max + bounded = Pattern.compile("^\\^([a-zA-Z0-9_-]+).*"); + } catch ( PatternSyntaxException res ) { res.printStackTrace(); System.out.println("Regular Expression bug in Ivy source code ... bailing out"); - System.exit(0); } } - public synchronized boolean CheckRegexp( String exp ) { - if (filter==null) return true; // there's no message filter - if (!bounded.match(exp)) return true; // the regexp is not bounded + + /** + * checks the "validity" of a regular expression if a filter has been set. + * @since 1.2.9 + * @param exp a string regular expression + * TODO must it be synchronized ( RE was not threadsafe, java regexp is ) + */ + public final boolean checkRegexp(final String exp) { + if (filter == null) return true; // there's no message filter + Matcher m = bounded.matcher(exp); + if (!m.matches()) return true; // the regexp is not bounded //System.out.println("the regexp is bounded, "+bounded.getParen(1)); // else the regexp is bounded. The matching string *must* be in the filter - for (int i=0;i<filter.length;i++) { - String prems = bounded.getParen(1); - // traceDebug(" classFilter ["+filter[i]+"] vs regexp ["+prems+"]"); - if (filter[i].compareTo(prems)==0) return true; - } + String prems = m.group(1); + for (String f: filter) if (f.compareTo(prems) == 0) return true; + // traceDebug(" classFilter ["+filter[i]+"] vs regexp ["+prems+"]"); return false; } // a private class used by bindMsgOnce, to ensure that a callback will be // executed once, and only once private class Once implements IvyMessageListener { - boolean received=false; - int id=-1; - IvyMessageListener callback=null; - Once(IvyMessageListener callback){this.callback=callback;} - void setRegexpId(int id){this.id=id;} - public void receive(IvyClient ic,String[] args){ - synchronized(this) { - // synchronized because it will most likely be called - // concurrently, and I *do* want to ensure that it won't - // execute twice - if (received||(callback==null)||(id==-1)) return; - received=true; - try {Ivy.this.unBindMsg(id);} catch (IvyException ie) { ie.printStackTrace(); } - callback.receive(ic,args); + private boolean received = false; + private int id = -1; + private IvyMessageListener ocallback = null; + Once(final IvyMessageListener callback){ ocallback = callback; } + synchronized void setRegexpId(final int fid){ id = fid; } + public void receive(final IvyClient ic , final String[] args){ + synchronized(Once.this) { + // synchronized because it will most likely be called + // concurrently, and I *do* want to ensure that it won't + // execute twice + if (received||(ocallback == null)||(id == -1)) return; + received = true; + try { Ivy.this.unBindMsg(id); } catch (IvyException ie) { ie.printStackTrace(); } + ocallback.receive(ic , args); } } } - + /* invokes the application listeners upon arrival of a new Ivy client */ - protected void clientConnects(IvyClient client){ - for ( int i = 0 ; i < ivyApplicationListenerList.size(); i++ ) { - ((IvyApplicationListener)ivyApplicationListenerList.elementAt(i)).connect(client); - } + protected synchronized final void clientConnects(final IvyClient client){ + for (IvyApplicationListener ial : ivyApplicationListenerList) ial.connect(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); - } + protected synchronized final void clientDisconnects(final IvyClient client){ + for (IvyApplicationListener ial : ivyApplicationListenerList) ial.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); - } + protected final void regexpReceived(final IvyClient client , final int id , final String sregexp){ + for (IvyBindListener ibl : ivyBindListenerList) ibl.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); - } + protected final void regexpDeleted(final IvyClient client , final int id , final String sregexp){ + for (IvyBindListener ibl : ivyBindListenerList) ibl.unbindPerformed(client , id , sregexp); } /* * invokes the application listeners when we are summoned to die * then stops */ - protected void dieReceived(IvyClient client, int id,String message){ - for ( int i=0 ;i<ivyApplicationListenerList.size();i++ ) { - ((IvyApplicationListener)ivyApplicationListenerList.elementAt(i)).die(client,id,message); - } + protected synchronized final void dieReceived(final IvyClient client , final int id , final String message){ + for (IvyApplicationListener ial : ivyApplicationListenerList) ial.die(client , id , message); } /* invokes the direct message callbacks */ - protected void directMessage( IvyClient client, int id,String msgarg ){ - for ( int i = 0 ; i < ivyApplicationListenerList.size(); i++ ) { - ((IvyApplicationListener)ivyApplicationListenerList.elementAt(i)).directMessage(client,id, msgarg); - } + protected synchronized final void directMessage(final IvyClient client , final int id , final String msgarg ){ + for (IvyApplicationListener ial : ivyApplicationListenerList) ial.directMessage(client , id, msgarg); } /** - * gives the (Vectored) list of IvyClient at a given instant + * gives the (Vectored) list of IvyClient at a given instant. + * @return a vector of IvyClients */ - public Vector getIvyClients() { - Vector v=new Vector(); - for (Enumeration e=clients.elements();e.hasMoreElements();) { - try { - IvyClient ic=(IvyClient)e.nextElement(); - if (ic!=null) v.addElement(ic); - } catch (ArrayIndexOutOfBoundsException _) { - continue; - } - } + public final Vector<IvyClient> getIvyClients() { + Vector<IvyClient> v = new Vector<IvyClient>(); + for (IvyClient ic : clients.values() ) if (ic != null) v.addElement(ic); return v; } /** - * gives a list of IvyClient with the name given in parameter + * gives a list of IvyClient with the name given in parameter. * * @param name The name of the Ivy agent you're looking for + * @return a vector of IvyClients */ - public Vector getIvyClientsByName(String name) { - Vector v=new Vector(); + public final Vector<IvyClient> getIvyClientsByName(final String name) { + Vector<IvyClient> v = new Vector<IvyClient>(); String icname; - for (Enumeration e=clients.elements();e.hasMoreElements();) { - try { - IvyClient ic = (IvyClient)e.nextElement(); - if ( (ic==null)||((icname=ic.getApplicationName())==null) ) break; - if (icname.compareTo(name)==0) v.addElement(ic); - } catch (ArrayIndexOutOfBoundsException _ ) { - continue; - } + for (IvyClient ic : clients.values() ) { + if ( (ic == null)||((icname = ic.getApplicationName()) == null) ) break; + if (icname.compareTo(name) == 0) v.addElement(ic); } return v; } /** - * returns the domain bus + * returns the domain bus. * * @param domainbus if non null, returns the argument * @return It returns domainbus, if non null, * otherwise it returns the IVYBUS property if non null, otherwise it * returns Ivy.DEFAULT_DOMAIN */ - public static String getDomain(String domainbus) { - if ( domainbus == null ) domainbus = System.getProperty("IVYBUS"); - if ( domainbus == null ) domainbus = DEFAULT_DOMAIN; - return domainbus; + public static final String getDomain(final String domainbus) { + String db = null; + db = domainbus; + if ( db == null ) db = System.getProperty("IVYBUS"); + if ( db == null ) db = DEFAULT_DOMAIN; + return db; } /** - * returns the domain bus + * returns the domain bus. * * @since 1.2.8 * @param progname The name of your program, for error message @@ -732,37 +782,35 @@ public class Ivy implements Runnable { * @return returns the domain bus, ascending priority : ivy default bus, IVY_BUS * property, -b domain on the command line */ - public static String getDomainArgs(String progname, String[] args) { - Getopt opt = new Getopt(progname,args,"b:"); + public static final String getDomainArgs(final String progname, final String[] args) { + Getopt opt = new Getopt(progname , args , "b:"); int c; - if ( ((c=opt.getopt())!=-1) && c=='b' ) return opt.getOptarg(); + if ( ((c = opt.getopt()) != -1) && c == 'b' ) return opt.getOptarg(); return getDomain(null); } /** - * returns a "wana be unique" ID to make requests on the bus + * returns a "wana be unique" ID to make requests on the bus. * * @since 1.2.8 * @return returns a string wich is meant to be noisy enough to be unique */ - public String getWBUId() { - return "ID<"+appName+myserial+":"+nextId()+":"+generator.nextInt()+">"; + public final String getWBUId() { + return "ID<" + appName + myserial + ":" + nextId() + ":" + generator.nextInt() + ">"; } private synchronized long nextId() { return current++; } /** - * prints a human readable representation of the list of domains + * prints a human readable representation of the list of domains. * * @since 1.2.9 */ public String domains(String toparse) { - String s=""; + StringBuffer s = new StringBuffer(); Ivy.Domain[] d = parseDomains(toparse); - for (int index=0;index<d.length;index++) { - s+=d[index].getDomainaddr()+":"+d[index].getPort()+" "; - } - return s; + for (Ivy.Domain dd : d) s.append(dd.getDomainaddr() + ":" + dd.getPort() + " "); + return s.toString(); } /////////////////////////////////////////////////////////////////: // @@ -770,57 +818,74 @@ public class Ivy implements Runnable { // /////////////////////////////////////////////////////////////////: - protected IvyClient createIvyClient(Socket s,int port, boolean domachin) throws IOException { - return new IvyClient(this,s,port,domachin); + protected IvyClient createIvyClient(Socket s , int port, boolean domachin) throws IOException { + setStarting(true); // this one will stop when the client has finished starting + IvyClient i = new IvyClient(this , s , port , domachin); + i.doStart(); + return i; } - protected synchronized void addClient(IvyClient c) { - if (clients==null||c==null) return; - synchronized (clients) { - clients.put(c.getClientKey(),c); - traceDebug("added "+c+" in clients: "+getClientNames(clients)); - } - } protected synchronized void removeClient(IvyClient c) { - synchronized (clients) { + synchronized(lock) { clients.remove(c.getClientKey()); - traceDebug("removed "+c+" from clients: "+getClientNames(clients)); + traceDebug("removed " + c + " from clients: " + getClientNames(clients)); } } - protected void addHalf(IvyClient c) { - synchronized(half){half.put(c.getClientKey(),c);} - traceDebug("added "+c+" in half: "+getClientNames(half)); + protected synchronized void handShake(IvyClient c) { + synchronized(lock) { + removeHalf(c); + if (clients == null||c == null) return; + // TODO check if it's not already here ! + IvyClient peer = searchPeer(c); + if ((peer == null) || peer.distanceTo(c)>0 ){ + clients.put(c.getClientKey() , c); + setStarting(false); + traceDebug("added " + c + " in clients: " + getClientNames(clients)); + } else { + traceDebug("not adding "+c+" in clients, double connexion detected, removing lowest one"); + try { + c.close(false); + } catch (IOException ioe) { + // TODO + } + } + } } - protected void removeHalf(IvyClient c) { - if (half==null||c==null) return; - synchronized(half){half.remove(c.getClientKey());} - traceDebug("removed "+c+" from half: "+getClientNames(half)); + protected synchronized void addHalf(IvyClient c) { + synchronized(lock){ half.put(c.getClientKey() , c); } + traceDebug("added " + c + " in half: " + getClientNames(half)); } - private boolean shouldIleave(IvyClient ic) { - traceDebug("looking for "+ic+" in "+getClientNames(half)+" and "+getClientNames(clients)); - IvyClient peer=searchPeer(ic); - if (peer==null) return false; - boolean shoulda=peer.compareTo(ic)>0; - traceDebug(ic+" "+ic.toStringExt()+((shoulda)?" must leave ":" must not leave")); - traceDebug(peer+" "+peer.toStringExt()+((!shoulda)?" must leave ":" must not leave")); + protected synchronized void removeHalf(IvyClient c) { + synchronized(lock) { + if (half == null||c == null) return; + half.remove(c.getClientKey()); + } + traceDebug("removed " + c + " from half: " + getClientNames(half)); + } + + /* + private synchronized boolean shouldIleave(IvyClient ic) { + traceDebug("looking for " + ic + " in " + getClientNames(half) + " and " + getClientNames(clients)); + IvyClient peer = searchPeer(ic); + if (peer == null) return false; + boolean shoulda = peer.distanceTo(ic)>0; + traceDebug(ic + " " + ic.toStringExt() + ((shoulda) ? " must leave " : " must not leave")); + traceDebug(peer + " " + peer.toStringExt() + ((!shoulda) ? " must leave " : " must not leave")); return shoulda; } + */ - private IvyClient searchPeer(IvyClient ic) { - IvyClient peer; - for (Enumeration e=half.elements();e.hasMoreElements();) { - peer=(IvyClient)e.nextElement(); - if ((peer!=null)&&(peer.equals(ic))) return peer; - } - synchronized (clients) { - for (Enumeration e=clients.elements();e.hasMoreElements();){ - peer=(IvyClient)e.nextElement(); - if ((peer!=null)&&(peer.equals(ic))) return peer; - } + private synchronized IvyClient searchPeer(IvyClient ic) { + synchronized(lock) { + //for (Enumeration<IvyClient> e = half.elements(); e.hasMoreElements(); ) { +// peer = e.nextElement(); +// if ((peer != null)&&(peer.equals(ic))) return peer; + // } + for (IvyClient peer : clients.values()) if ((peer != null)&&(peer.equals(ic))) return peer; } return null; } @@ -830,78 +895,80 @@ public class Ivy implements Runnable { */ public void run() { traceDebug("service thread started"); // THREADDEBUG - Thread thisThread=Thread.currentThread(); - while(thisThread==serverThread){ + Thread thisThread = Thread.currentThread(); + Socket socket = null; + while ( thisThread == serverThread ){ try { - Socket socket = app.accept(); - if ((thisThread!=serverThread)||stopped) break; // early disconnexion - createIvyClient(socket,0,true); // the peer called me + synchronized (this) { + //System.out.println("DEBUG stopped: "+stopped); + if ((thisThread != serverThread)||stopped) break; // early disconnexion + } + synchronized (lockApp) { + socket = app.accept(); // TODO I can't synchronize on (this) in the run + } + synchronized (this) { + if ((thisThread != serverThread)||stopped) break; // early disconnexion + createIvyClient(socket , 0 , true); // the peer called me + } } catch (InterruptedIOException ie) { - // traceDebug("server socket was interrupted. good"); - if (thisThread!=serverThread) break; + // traceDebug("server socket was interrupted. good"); + if (thisThread != serverThread) break; } catch( IOException e ) { - if (serverThread==thisThread) { + if (serverThread == thisThread) { traceDebug("Error IvyServer exception: " + e.getMessage()); - System.out.println("Ivy server socket reader caught an exception " + e.getMessage()); - System.out.println("this is probably a bug in your JVM ! (e.g. blackdown jdk1.1.8 linux)"); - System.exit(0); - } else { - traceDebug("my server socket has been closed"); - } + System.out.println("Ivy server socket reader caught an exception " + e.getMessage()); + System.out.println("this is probably a bug in your JVM ! (e.g. blackdown jdk1.1.8 linux)"); + throw new RuntimeException(); + } else { + traceDebug("my server socket has been closed"); + } } } traceDebug("service thread stopped"); // THREADDEBUG } - protected String getWatcherId() { return watcherId ; } + protected String getAppName() { return appName; } + protected int getAppPort() { return applicationPort; } + protected String getReadyMessage() { return ready_message; } + protected boolean getProtectNewlines() { return doProtectNewlines; } + + protected void setStarting(boolean s) { + synchronized(readyToSend) { + traceDebug("setStarting "+s); + starting = s; + } + } + protected String getWatcherId() { return watcherId; } - protected int getSerial() { return myserial ; } + protected int getSerial() { return myserial; } private void traceDebug(String s){ - if (debug) System.out.println("-->Ivy["+myserial+"]<-- "+s); + if (debug) System.out.println("-->Ivy[" + myserial + "]<-- " + s); } // stuff to guarantee that all the treads have left synchronized void registerThread(Thread t) { sendThreads.addElement(t); } synchronized void unRegisterThread(Thread t) { sendThreads.removeElement(t); } synchronized Thread getOneThread() { - if (sendThreads.size()==0) return null; + if (sendThreads.size() == 0) return null; return (Thread) sendThreads.firstElement(); } // a small private method for debbugging purposes - private String getClientNames(Hashtable t) { - String s = "("; - for (Enumeration e=t.elements();e.hasMoreElements();){ - IvyClient ic = (IvyClient)e.nextElement(); - if (ic!=null) s+=ic.getApplicationName()+","; - } - return s+")"; - } - - private class Domain { - String domainaddr; - int port; - public Domain(String domainaddr,int port) {this.domainaddr=domainaddr;this.port=port;} - public String toString() {return domainaddr+":"+port;} + private String getClientNames(Hashtable<Integer , IvyClient> t) { + StringBuffer s = new StringBuffer(); + s.append("("); + for (IvyClient ic : t.values() ) if (ic != null) s.append(ic.getApplicationName() + ","); + s.append(")"); + return s.toString(); + } + + private static class Domain { + private String domainaddr; + private int port; + public Domain(String ddomainaddr , int dport) { this.domainaddr = ddomainaddr;this.port = dport; } + public String toString() { return domainaddr + ":" + port; } public String getDomainaddr() { return domainaddr; } public int getPort() { return port; } } - // test. Normally, running java fr.dgac.ivy.Ivy should stop in 2.3 seconds :) - public static void main(String[] args) { - Ivy bus = new Ivy("Test Unitaire","TU ready",null); - try { - bus.start(Ivy.getDomainArgs("IvyTest",args)); - 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"); - System.out.println("random values: "+bus.getWBUId()+", "+bus.getWBUId()+", "+bus.getWBUId()); - bus.stop(); - } catch (IvyException ie) { - System.out.println("Ivy main test error"); - ie.printStackTrace(); - } - } - } // class Ivy diff --git a/src/IvyApplicationAdapter.java b/src/IvyApplicationAdapter.java index 0a82486..75cfa40 100755 --- a/src/IvyApplicationAdapter.java +++ b/src/IvyApplicationAdapter.java @@ -1,7 +1,7 @@ /** * this class is a dummy ApplicationListener * - * @author François-Régis Colin + * @author Francois-Rzgis Colin * @author Yannick Jestin * @author <a href="http://www.tls.cena.fr/products/ivy/">http://www.tls.cena.fr/products/ivy/</a> * diff --git a/src/IvyApplicationListener.java b/src/IvyApplicationListener.java index 7f7ad3f..f4894bf 100755 --- a/src/IvyApplicationListener.java +++ b/src/IvyApplicationListener.java @@ -3,7 +3,7 @@ package fr.dgac.ivy; /** * this interface specifies the methods of an ApplicationListener * - * @author François-Régis Colin + * @author Francois-Rzgis Colin * @author Yannick Jestin * @author <a href="http://www.tls.cena.fr/products/ivy/">http://www.tls.cena.fr/products/ivy/</a> * diff --git a/src/IvyClient.java b/src/IvyClient.java index b723433..8ed25f7 100755 --- a/src/IvyClient.java +++ b/src/IvyClient.java @@ -10,6 +10,18 @@ * created for each remote client. * * CHANGELOG: + * 1.2.14 + * - use autoboxing for the creation of Integer (instead of + * new Integer(int). This alows caching, avoids object allocation, and the + * code will be faster + * - removed the synchronized on boxed primitive (Integer(0) for lock, which + * could be cached and reused elsewhere). Lock is now a new Object() + * - remove the Thread.start() from the constructor, to avoid mulithread issues + * see * http://findbugs.sourceforge.net/bugDescriptions.html#SC_START_IN_CTOR + * now ,we have to call IvyClient.start() after it has been created + * - add generic types to declarations + * - remove sendBye(), which is never called + * - switch from gnu regexp (deprecated) to the built in java regexp * 1.2.12 * - Ping and Pong are back ... * 1.2.8 @@ -78,9 +90,9 @@ import java.lang.Thread; import java.net.*; import java.io.*; import java.util.*; -import org.apache.regexp.*; +import java.util.regex.*; -public class IvyClient implements Runnable { +public class IvyClient extends Thread { /* the protocol magic numbers */ final static int Bye = 0; /* end of the peer */ @@ -105,11 +117,10 @@ public class IvyClient implements Runnable { // private variables private final static int MAXPONGCALLBACKS = 10; private static int pingSerial = 0; - private static Integer csMutex=new Integer(0); + private static final Object lock = new Object(); private static int clientSerial=0; /* an unique ID for each IvyClient */ - private Hashtable PingCallbacksTable = new Hashtable(); + private Hashtable <Integer,PingCallbackHolder>PingCallbacksTable = new Hashtable<Integer,PingCallbackHolder>(); - private String messages_classes[] = null; private Ivy bus; private Socket socket; private BufferedReader in; @@ -122,8 +133,8 @@ public class IvyClient implements Runnable { // protected variables String appName="none"; - Hashtable regexps = new Hashtable(); - Hashtable regexpsText = new Hashtable(); + Hashtable <Integer,Pattern>regexps = new Hashtable<Integer,Pattern>(); + Hashtable <Integer,String>regexpsText = new Hashtable<Integer,String>(); static boolean debug = (System.getProperty("IVY_DEBUG")!=null) ; // int protocol; private boolean incoming; @@ -131,7 +142,7 @@ public class IvyClient implements Runnable { IvyClient() { } IvyClient(Ivy bus, Socket socket,int remotePort,boolean incoming) throws IOException { - synchronized(csMutex) { clientKey=new Integer(clientSerial++); } + synchronized(lock) { clientKey=clientSerial++; } this.bus = bus; this.remotePort = remotePort; this.incoming = incoming; @@ -149,6 +160,12 @@ public class IvyClient implements Runnable { } remoteHostname = socket.getInetAddress().getHostName(); clientThread = new Thread(this); // clientThread handles the incoming traffic + } + + /* removed from the constructor, to avoid Mulithread correctnaess issuses + * see http://findbugs.sourceforge.net/bugDescriptions.html#SC_START_IN_CTOR + */ + protected void doStart() { clientThread.start(); } @@ -157,24 +174,16 @@ public class IvyClient implements Runnable { // information is in the socket itself, the port is not known if we // initiate the connexion private void sendSchizo() throws IOException { - traceDebug("sending our service port "+bus.applicationPort); - Hashtable tosend=bus.selfIvyClient.regexpsText; - sendString(SchizoToken,bus.applicationPort,bus.appName); - for (Enumeration e = tosend.keys(); e.hasMoreElements(); ) { - Integer ikey = (Integer)e.nextElement(); - sendRegexp(ikey.intValue(),(String)tosend.get(ikey)); - } + traceDebug("sending our service port "+bus.getAppPort()); + Hashtable<Integer,String> tosend=bus.getSelfIvyClient().regexpsText; + sendString(SchizoToken,bus.getAppPort(),bus.getAppName()); + for (Integer ikey : tosend.keySet()) sendRegexp(ikey.intValue(),tosend.get(ikey)); sendString( EndRegexp,0,""); } - synchronized private void handShake() throws IvyException { - synchronized(bus) { - bus.removeHalf(this); - bus.addClient(this); - } - } + public String toString() { + return "IC["+clientKey+","+bus.getSerial()+"] "+bus.getAppName()+":"+appName+":"+remotePort; } - public String toString() { return "IC["+clientKey+","+bus.getSerial()+"] "+bus.appName+":"+appName+":"+remotePort; } public String toStringExt() { return "client socket:"+socket+", remoteport:" + remotePort; } @@ -197,9 +206,8 @@ public class IvyClient implements Runnable { * allow the notification of regexp addition and deletion * The content is not modifyable because String are not mutable, and cannot * be modified once they are create. - * @see IvyClient#getRegexpsArray() getRegexpsArray to get a String[] result */ - public Enumeration getRegexps() { return regexpsText.elements(); } + public Enumeration<String> getRegexps() { return regexpsText.elements(); } /** * allow an Ivy package class to access the list of regexps at a @@ -209,8 +217,8 @@ public class IvyClient implements Runnable { public String[] getRegexpsArray() { String[] s = new String[regexpsText.size()]; int i=0; - for (Enumeration e=getRegexps();e.hasMoreElements();) - s[i++]=(String)e.nextElement(); + for (Enumeration<String>e=getRegexps();e.hasMoreElements();) + s[i++]=e.nextElement(); return s; } @@ -265,15 +273,13 @@ public class IvyClient implements Runnable { protected int sendMsg(String message) { int count = 0; - for (Enumeration e = regexps.keys();e.hasMoreElements();) { - Integer key = (Integer)e.nextElement(); - RE regexp = (RE)regexps.get(key); + for (Integer key : regexps.keySet()) { + Pattern regexp = regexps.get(key); synchronized (regexp) { - // re.match fails sometimes when it is called concurrently .. - // see 28412 on jakarta regexp bugzilla - if (regexp.match(message)) { + Matcher m = regexp.matcher(message); + if (m.matches()) { count++; // match - sendResult(Msg,key,regexp); + sendResult(Msg,key,m); } } } @@ -298,13 +304,13 @@ public class IvyClient implements Runnable { * compares two peers the id is the couple (host,service port). * true if the peers are similar. This should not happen, it is bad */ - protected int compareTo(IvyClient clnt) { + protected int distanceTo(IvyClient clnt) { // return clnt.clientKey.compareTo(clientKey); // Wrong. it's random... return (clnt.socket.getPort()-socket.getLocalPort()); } protected boolean equals(IvyClient clnt) { - if (clnt==this) return false; + if (clnt==this) return true; // TODO go beyond the port number ! add some host processing, cf: // IvyWatcher ... if (remotePort==clnt.remotePort) return true; @@ -339,7 +345,7 @@ public class IvyClient implements Runnable { break; } } else { - traceDebug("readline null ! leaving the thead"); + traceDebug("readline null ! leaving the thread"); break; } } catch (IvyException ie) { @@ -355,7 +361,7 @@ public class IvyClient implements Runnable { break; } } - traceDebug("normally Disconnected from "+ appName); + traceDebug("normally Disconnected from "+ bus.getAppName()); bus.removeClient(this); // invokes the disconnect applicationListeners if (!discCallbackPerformed) bus.clientDisconnects(this); @@ -363,6 +369,15 @@ public class IvyClient implements Runnable { traceDebug("Thread stopped"); } + @Override public void interrupt(){ + super.interrupt(); + try { + if (socket!=null) socket.close(); + } catch (IOException ioe) { + ioe.printStackTrace(); + } + } + protected synchronized void sendBuffer( String buffer ) throws IvyException { buffer += "\n"; try { @@ -392,40 +407,52 @@ public class IvyClient implements Runnable { } } - private void sendResult(int type,Integer id, RE regexp) { + private void sendResult(int type,Integer id, Matcher m) { try { - String buffer = type+" "+id+StartArg; - for(int i=1;i<regexp.getParenCount();i++) - buffer+=regexp.getParen(i)+EndArg; - sendBuffer(buffer); + StringBuffer buffer = new StringBuffer(); + buffer.append(type); + buffer.append(" "); + buffer.append(id); + buffer.append(StartArg); + for(int i=1;i<=m.groupCount();i++){ + buffer.append(m.group(i)); + buffer.append(EndArg); + } + sendBuffer(buffer.toString()); } catch (IvyException ie ) { System.err.println("received an exception: " + ie.getMessage()); ie.printStackTrace(); } catch (StringIndexOutOfBoundsException sioobe) { - System.out.println("arg: "+regexp.getParenCount()+" "+regexp); + System.out.println("arg: "+m.groupCount()+" "+m); sioobe.printStackTrace(); } } private String dumpHex(String s) { byte[] b = s.getBytes(); - String outDump = ""; - String zu = "\t"; + StringBuffer outDump = new StringBuffer(); + StringBuffer zu = new StringBuffer("\t"); for (int i=0;i<b.length;i++) { char c = s.charAt(i); - outDump+=((int)c) + " "; - zu+= ((c>15) ? c : 'X')+" "; + outDump.append(((int)c)); + outDump.append(" "); + zu.append((c>15) ? c : 'X'); + zu.append(" "); } - outDump += zu; - return outDump; + outDump.append(zu); + return outDump.toString(); } private String dumpMsg(String s) { - String deb = " \""+s+"\" "+s.length()+" cars, "; + StringBuffer deb = new StringBuffer(" \""+s+"\" "+s.length()+" cars, "); for (int i=0;i<s.length();i++) { - deb+= "["+s.charAt(i) + "]:" + (int)s.charAt(i) +", "; + deb.append("["); + deb.append(s.charAt(i)); + deb.append("]:"); + deb.append(s.charAt(i)); + deb.append(", "); } - return s; + return deb.toString(); } protected boolean newParseMsg(String s) throws IvyException { @@ -496,12 +523,12 @@ public class IvyClient implements Runnable { break; case AddRegexp: String regexp=s.substring(from,b.length); - if ( bus.CheckRegexp(regexp) ) { + if ( bus.checkRegexp(regexp) ) { try { - regexps.put(msgId,new RE(regexp)); + regexps.put(msgId,Pattern.compile(regexp,Pattern.DOTALL)); regexpsText.put(msgId,regexp); bus.regexpReceived(this,msgId.intValue(),regexp); - } catch (RESyntaxException e) { + } catch (PatternSyntaxException e) { // the remote client sent an invalid regexp ! traceDebug("invalid regexp sent by " +appName+" ("+regexp+"), I will ignore this regexp"); sendBuffer(Error+e.toString()); @@ -519,10 +546,11 @@ public class IvyClient implements Runnable { break; case EndRegexp: bus.clientConnects(this); - if (bus.ready_message!=null) sendMsg(bus.ready_message); + String srm = bus.getReadyMessage(); + if (srm!=null) sendMsg(srm); break; case Msg: - Vector v = new Vector(); + Vector <String>v = new Vector<String>(); while (to<b.length) { while ( (to<b.length) && (b[to]!=3) ) to++; if (to<b.length) { @@ -532,10 +560,11 @@ public class IvyClient implements Runnable { } } String[] tab = new String[v.size()]; - for (int i=0;i<v.size();i++) tab[i]=(String)v.elementAt(i); + int i=0; + for (String st: v) tab[i++]=st; // for developpemnt purposes traceDebug(tab); - bus.selfIvyClient.callCallback(this,msgId,tab); + bus.getSelfIvyClient().callCallback(this,msgId,tab); break; case Error: String error=s.substring(from,b.length); @@ -551,7 +580,7 @@ public class IvyClient implements Runnable { try { bus.addHalf(this); sendSchizo(); - handShake(); + bus.handShake(this); } catch (IOException ioe) { throw new IvyException(ioe.toString()); } @@ -559,7 +588,7 @@ public class IvyClient implements Runnable { } else { // outgoing connexion // I already have sent him a token - handShake(); + bus.handShake(this); } break; case DirectMsg: @@ -573,7 +602,7 @@ public class IvyClient implements Runnable { return true; } - private void sendBye() {sendString(Bye,0,"");} + //private void sendBye() {sendString(Bye,0,"");} private void sendBye(String message) {sendString(Bye,0,message);} private void traceDebug(String s){ @@ -581,24 +610,30 @@ public class IvyClient implements Runnable { int serial=0; if (bus!=null) { serial=bus.getSerial(); - app=bus.appName; + app=bus.getAppName(); } if (debug) System.out.println("-->IvyClient["+clientKey+","+serial+"] "+app+" (remote "+appName+")<-- "+s); } private void traceDebug(String[] tab){ - String s = " string array " + tab.length + " elements: "; - for (int i=0;i<tab.length;i++) s+="("+tab[i]+") "; - traceDebug(s); + StringBuffer s = new StringBuffer(" string array "); + s.append(tab.length); + s.append(" elements: "); + for (String ss: tab) { + s.append("("); + s.append(ss); + s.append(") "); + } + traceDebug(s.toString()); } void PCHadd(int serial,PingCallback pc) { - PingCallbacksTable.put(new Integer(serial),new PingCallbackHolder(pc)); + PingCallbacksTable.put(serial,new PingCallbackHolder(pc)); if (PingCallbacksTable.size()>MAXPONGCALLBACKS) { // more than MAXPONGCALLBACKS callbacks, we ought to limit to prevent a // memory leak // TODO remove the first - Integer smallest=(Integer)new TreeSet(PingCallbacksTable.keySet()).first(); + Integer smallest=(Integer)new TreeSet<Integer>(PingCallbacksTable.keySet()).first(); PingCallbackHolder pch = (PingCallbackHolder)PingCallbacksTable.remove(smallest); System.err.println("no response from "+getApplicationName()+" to ping "+smallest+" after "+pch.age()+" ms, discarding"); } @@ -626,4 +661,11 @@ public class IvyClient implements Runnable { } } + public static void main(String[] args) { + String s="hello\nworld"; + String dest=encode(s); + System.out.println("avant: <"+s+">\naprès: <"+dest+">"); + System.out.println("tailles: "+s.length()+" "+dest.length()); + } + } diff --git a/src/IvyDaemon.java b/src/IvyDaemon.java index 71a2b93..3ec54e6 100644 --- a/src/IvyDaemon.java +++ b/src/IvyDaemon.java @@ -11,6 +11,11 @@ * (c) CENA * * changelog: + * 1.2.14 + * - remove the Thread.start() from the constructor, to avoid mulithread issues + * see * http://findbugs.sourceforge.net/bugDescriptions.html#SC_START_IN_CTOR + * now ,we have to call IvyClient.start() after it has been created + * - gracefully quits when message is received, by quitting the Ivy * 1.2.8 * - goes into tools subpackage * 1.2.3 @@ -35,12 +40,11 @@ public class IvyDaemon implements Runnable { private ServerSocket serviceSocket; - private boolean isRunning=false; private static boolean debug = (System.getProperty("IVY_DEBUG")!=null) ; private volatile Thread clientThread;// volatile to ensure the quick communication private Ivy bus; - public static int DEFAULT_SERVICE_PORT = 3456 ; + public static final int DEFAULT_SERVICE_PORT = 3456 ; public static final String DEFAULTNAME = "IvyDaemon"; public static final String helpmsg = "usage: java fr.dgac.ivy.tools.IvyDaemon [options]\n\t-b BUS\tspecifies the Ivy bus domain\n\t-p\tport number, default "+DEFAULT_SERVICE_PORT+"\n\t-n ivyname (default "+DEFAULTNAME+")\n\t-q\tquiet, no tty output\n\t-d\tdebug\n\t-h\thelp\nListens on the TCP port, and sends each line read on the Ivy bus. It is useful to launch one Ivy Daemon and let scripts send their message on the bus.\n"; @@ -72,25 +76,28 @@ public class IvyDaemon implements Runnable { servicePort = Integer.parseInt(s=opt.getOptarg()); } catch (NumberFormatException nfe) { System.out.println("Invalid port number: " + s ); - System.exit(0); + return; } break; case 'h': default: System.out.println(helpmsg); - System.exit(0); + return; } bus=new Ivy(name,name+" ready",null); if (!quiet) System.out.println("broadcasting on "+bus.domains(domain)); bus.start(domain); if (!quiet) System.out.println("listening on "+servicePort); - IvyDaemon d = new IvyDaemon(bus,servicePort); + new IvyDaemon(bus,servicePort).doStart(); } public IvyDaemon(Ivy bus,int servicePort) throws IOException { this.bus=bus; serviceSocket = new ServerSocket(servicePort) ; clientThread=new Thread(this); + } + + protected void doStart() { clientThread.start(); } @@ -113,10 +120,12 @@ public class IvyDaemon implements Runnable { class SubReader extends Thread { BufferedReader in; + SubReader(Socket socket) throws IOException { in=new BufferedReader(new InputStreamReader(socket.getInputStream())); - start(); + SubReader.this.start(); } + public void run() { traceDebug("Subreader Thread started"); String msg = null; @@ -133,7 +142,7 @@ public class IvyDaemon implements Runnable { } catch (IOException ioe) { traceDebug("Subreader exception ..."); ioe.printStackTrace(); - System.exit(0); + throw new RuntimeException(); } traceDebug("Subreader Thread stopped"); } diff --git a/src/IvyException.java b/src/IvyException.java index bd052b6..46c872f 100755 --- a/src/IvyException.java +++ b/src/IvyException.java @@ -3,7 +3,7 @@ package fr.dgac.ivy; /** * signals that an unrecoverrable Ivy exception has occured. * - * @author François-Régis Colin + * @author Francois-Rzgis Colin * @author Yannick Jestin * @author <a href="http://www.tls.cena.fr/products/ivy/">http://www.tls.cena.fr/products/ivy/</a> * diff --git a/src/IvyHttpGatewayClient.java b/src/IvyHttpGatewayClient.java index 585e342..5c602d3 100644 --- a/src/IvyHttpGatewayClient.java +++ b/src/IvyHttpGatewayClient.java @@ -21,7 +21,6 @@ import java.net.*; * See IvyHttpGatewayServlet * * @see IvyHttpGatewayServlet - * @see fr.dgac.ivy * @author Francis JAMBON - CLIPS-IMAG/MultiCom * @version 1.0.5 */ diff --git a/src/IvyHttpGatewayServlet.java b/src/IvyHttpGatewayServlet.java index ca886b5..fd22bd2 100644 --- a/src/IvyHttpGatewayServlet.java +++ b/src/IvyHttpGatewayServlet.java @@ -64,14 +64,13 @@ import fr.dgac.ivy.*; * * GNU Lesser General Public License, see <a target="_blank" href="http://www.gnu.org/copyleft/lesser.html">http://www.gnu.org/copyleft/lesser.html</a> * - * @see fr.dgac.ivy * @author Francis JAMBON - CLIPS-IMAG/MultiCom * @version 1.1 */ public class IvyHttpGatewayServlet extends HttpServlet { // Ivy bus(es) list - private Hashtable buses; + private Hashtable<IvyHashKey,Ivy> buses; // Valid name, domain and msg regexps private static final String NAME_REGEXP="[^(\\r)]+"; @@ -89,7 +88,7 @@ public class IvyHttpGatewayServlet extends HttpServlet { */ public void init(ServletConfig config) throws ServletException { super.init(config); - this.buses = new Hashtable(); + this.buses = new Hashtable<IvyHashKey,Ivy>(); } @@ -97,10 +96,7 @@ public class IvyHttpGatewayServlet extends HttpServlet { * Destroys the servlet. */ public void destroy() { - Enumeration eb = this.buses.elements(); - while (eb.hasMoreElements()) { - ((Ivy)eb.nextElement()).stop(); - } + for (Ivy eb : this.buses.values() ) eb.stop(); // wait 100ms to prevent ugly Ivy bus(es) disconnection try { Thread.sleep(100); } catch (InterruptedException ie) {} this.buses.clear(); @@ -324,9 +320,7 @@ public class IvyHttpGatewayServlet extends HttpServlet { out.println(); out.println(this.buses.size()+" Ivy bus(es) alive"); - Enumeration ek = this.buses.keys(); - while (ek.hasMoreElements()) { - IvyHashKey ihk = (IvyHashKey)(ek.nextElement()); + for (IvyHashKey ihk : this.buses.keySet()) { out.println("- Ivy bus ["+ihk.getName()+ "] on domain ["+ihk.getDomain()+ "] managed by ["+ihk.getFrom()+"]"); @@ -362,14 +356,8 @@ public class IvyHttpGatewayServlet extends HttpServlet { out.println(); if (this.buses.size()!=0) { - - Enumeration eb = this.buses.elements(); - while (eb.hasMoreElements()) { - ((Ivy)eb.nextElement()).stop(); - } - Enumeration ek = this.buses.keys(); - while (ek.hasMoreElements()) { - IvyHashKey ihk = (IvyHashKey)(ek.nextElement()); + for (Ivy eb : this.buses.values() ) eb.stop(); + for ( IvyHashKey ihk : this.buses.keySet()) { out.println("Ivy bus ["+ihk.getName()+ "] on domain ["+ihk.getDomain()+ "] managed by ["+ihk.getFrom()+ diff --git a/src/IvyHttpGatewayTest.java b/src/IvyHttpGatewayTest.java index e8de0aa..d4cf19f 100644 --- a/src/IvyHttpGatewayTest.java +++ b/src/IvyHttpGatewayTest.java @@ -16,7 +16,6 @@ package fr.dgac.ivy.tools; * * @see IvyHttpGatewayClient * @see IvyHttpGatewayServlet - * @see fr.dgac.ivy * @author Francis JAMBON - CLIPS-IMAG/MultiCom * @version See IvyHttpGatewayClient */ diff --git a/src/IvyMessageListener.java b/src/IvyMessageListener.java index 1a8d8a5..de722d5 100755 --- a/src/IvyMessageListener.java +++ b/src/IvyMessageListener.java @@ -3,7 +3,7 @@ package fr.dgac.ivy; /** * this interface specifies the methods of an IvyMessageListener * - * @author François-Régis Colin + * @author Francois-Rzgis Colin * @author Yannick Jestin * @author <a href="http://www.tls.cena.fr/products/ivy/">http://www.tls.cena.fr/products/ivy/</a> * diff --git a/src/IvyWatcher.java b/src/IvyWatcher.java index 3281079..5a47da3 100755 --- a/src/IvyWatcher.java +++ b/src/IvyWatcher.java @@ -2,7 +2,7 @@ * IvyWatcher, A private Class for the Ivy rendezvous * * @author Yannick Jestin - * @author François-Régis Colin + * @author Francois-Regis Colin * @author <a href="http://www.tls.cena.fr/products/ivy/">http://www.tls.cena.fr/products/ivy/</a> * * (C) CENA @@ -14,6 +14,13 @@ * thing. * * CHANGELOG: + * 1.2.14 + * - tries to fix a lock on accept() by becoming a Thread instead of + * runnalbe (see tests/test2) + * - removed unread field (domainaddr, e.g.) + * - throws RuntimeException instead of System.exit(), allows code reuse + * - switch from gnu regexp (deprecated) to the built in java regexp + * - add generic types to declarations * 1.2.13: * - TCP_NO_DELAY to disable Nagle's algorithm * - private static ?! pour already present ... @@ -73,17 +80,14 @@ package fr.dgac.ivy ; import java.lang.Thread; import java.net.*; import java.io.*; -import org.apache.regexp.*; import java.util.Hashtable; +import java.util.regex.*; -class IvyWatcher implements Runnable { +class IvyWatcher extends Thread { private static boolean debug = (System.getProperty("IVY_DEBUG")!=null); - private boolean isMulticastAddress = false; private boolean alreadyIgnored = false; private Ivy bus; /* master bus controler */ private DatagramSocket broadcast; /* supervision socket */ - private InetAddress localhost,loopback; - private String domainaddr; private int port; private volatile Thread listenThread = null; private InetAddress group; @@ -91,6 +95,8 @@ class IvyWatcher implements Runnable { private int myserial=serial++; private String busWatcherId = null; + private static Pattern recoucou, numbersPoint, exp; + /** * creates an Ivy watcher * @param bus the bus @@ -98,7 +104,6 @@ class IvyWatcher implements Runnable { */ IvyWatcher(Ivy bus,String domainaddr,int port) throws IvyException { this.bus = bus; - this.domainaddr=domainaddr; this.port=port; busWatcherId=bus.getWatcherId(); listenThread = new Thread(this); @@ -106,13 +111,8 @@ class IvyWatcher implements Runnable { try { group = InetAddress.getByName(domainaddr); broadcast = new MulticastSocket(port); - if (group.isMulticastAddress()) { - isMulticastAddress = true; - ((MulticastSocket)broadcast).joinGroup(group); - } + if (group.isMulticastAddress()) ((MulticastSocket)broadcast).joinGroup(group); broadcast.setSoTimeout(Ivy.TIMEOUTLENGTH); - localhost=InetAddress.getLocalHost(); - loopback=InetAddress.getByName(null); } catch ( UnknownHostException uhe ) { } catch ( IOException e ) { throw new IvyException("IvyWatcher I/O error" + e ); @@ -134,35 +134,35 @@ class IvyWatcher implements Runnable { byte buf[] = new byte[256]; DatagramPacket packet=new DatagramPacket(buf,buf.length); broadcast.receive(packet); + bus.setStarting(true); // someone's coming if (listenThread!=thisThread) break; // I was summoned to leave during the receive String msg = new String(buf,0,packet.getLength()); String remotehostname=null; try { remotehost = packet.getAddress(); remotehostname = remotehost.getHostName(); - RE re = new RE("([0-9]*) ([0-9]*)"); - if (!(re.match(msg))) { - System.err.println("Ignoring bad format broadcast from "+ - remotehostname+":"+packet.getPort()); + Matcher m = recoucou.matcher(msg); + if (!m.matches()) { + System.err.println("Ignoring bad format broadcast from "+ remotehostname+":"+packet.getPort()); continue; } - int version = Integer.parseInt(re.getParen(1)); + int version = Integer.parseInt(m.group(1)); if ( version < Ivy.PROTOCOLMINIMUM ) { System.err.println("Ignoring bad format broadcast from "+ remotehostname+":"+packet.getPort() +" protocol version "+remotehost+" we need "+Ivy.PROTOCOLMINIMUM+" minimum"); continue; } - remotePort = Integer.parseInt(re.getParen(2)); - if (bus.applicationPort==remotePort) { // if (same port number) - RE reId = new RE("([0-9]*) ([0-9]*) ([^ ]*) (.*)"); - if (reId.match(msg)&&(busWatcherId!=null)) { - traceDebug("there's an appId: "+reId.getParen(3)); - String otherId=reId.getParen(3); - String otherName=reId.getParen(4); + remotePort = Integer.parseInt(m.group(2)); + if (bus.getAppPort()==remotePort) { // if (same port number) + if (busWatcherId!=null) { + traceDebug("there's an appId: "+m.group(3)); + String otherId=m.group(3); + String otherName=m.group(4); if (busWatcherId.compareTo(otherId)==0) { // same port #, same bus Id, It's me, I'm outta here traceDebug("ignoring my own broadcast"); + bus.setStarting(false); continue; } else { // same port #, different bus Id, it's another agent @@ -199,9 +199,6 @@ class IvyWatcher implements Runnable { } else { traceDebug("there is already a request originating from " + remotehost + ":" + remotePort); } - } catch (RESyntaxException ree) { - ree.printStackTrace(); - System.exit(-1); } catch (NumberFormatException nfe) { System.err.println("Ignoring bad format broadcast from "+remotehostname); continue; @@ -225,15 +222,23 @@ class IvyWatcher implements Runnable { traceDebug("Thread stopped"); // THREADDEBUG } + @Override public void interrupt(){ + super.interrupt(); + broadcast.close(); + } + /** * stops the thread waiting on the broadcast socket */ - synchronized void stop() { + synchronized void doStop() { traceDebug("begining stopping"); + bus.setStarting(true); Thread t = listenThread; listenThread=null; + interrupt(); broadcast.close(); if (t!=null) { t.interrupt(); } // it might not even have been created + bus.setStarting(false); traceDebug("stopped"); } @@ -242,8 +247,10 @@ class IvyWatcher implements Runnable { // domain. DatagramPacket packet; String data; - public PacketSender(String data) { + Ivy bus; + public PacketSender(String data, Ivy b) { this.data=data; + bus = b; packet=new DatagramPacket(data.getBytes(),data.length(),group,port); new Thread((PacketSender.this)).start(); } @@ -263,15 +270,17 @@ class IvyWatcher implements Runnable { e.printStackTrace(); } } + try { Thread.sleep(100); } catch (InterruptedException ie ){ } + bus.setStarting(false); // one of the senders has finished its work, plus extra time traceDebug("PacketSender thread stopped"); // THREADDEBUG } } - synchronized void start() throws IvyException { - // String hello = Ivy.PROTOCOLVERSION + " " + bus.applicationPort + "\n"; - String hello = Ivy.PROTOCOLVERSION + " " + bus.applicationPort + " "+busWatcherId+" "+bus.selfIvyClient.getApplicationName()+"\n"; + synchronized void doStart() throws IvyException { + // String hello = Ivy.PROTOCOLVERSION + " " + bus.getAppPort() + "\n"; + String hello = Ivy.PROTOCOLVERSION + " " + bus.getAppPort() + " "+busWatcherId+" "+bus.getSelfIvyClient().getApplicationName()+"\n"; if (broadcast==null) throw new IvyException("IvyWatcher PacketSender null broadcast address"); - new PacketSender(hello); // notifies our arrival on each domain: protocol version + port + new PacketSender(hello,bus); // notifies our arrival on each domain: protocol version + port listenThread.start(); } @@ -284,13 +293,13 @@ class IvyWatcher implements Runnable { * regoes static ... */ //private static Hashtable alreadySocks=new Hashtable(); - private Hashtable alreadySocks=new Hashtable(); + private Hashtable<String,Integer> alreadySocks=new Hashtable<String,Integer>(); private synchronized boolean alreadyBroadcasted(String s,int port) { // System.out.println("DEBUUUUUUUG " + s+ ":" + port); if (s==null) return false; - Integer i = (Integer)alreadySocks.get(s); - if (((i!=null)&&(i.compareTo(new Integer(port)))==0)) return true; - alreadySocks.put(s,new Integer(port)); + Integer i = alreadySocks.get(s); + if (((i!=null)&&(i.compareTo(port))==0)) return true; + alreadySocks.put(s,port); return false; } @@ -322,22 +331,22 @@ class IvyWatcher implements Runnable { int sep_index = net.lastIndexOf( ":" ); if ( sep_index != -1 ) { net = net.substring(0,sep_index); } try { - RE numbersPoint = new RE("([0-9]|\\.)+"); - if (!numbersPoint.match(net)) { + Matcher m = numbersPoint.matcher(net); + if (!m.matches()) { // traceDebug("should only have numbers and point ? I won't add anything... " + net); return "127.255.255.255"; // return net; } net += ".255.255.255"; - RE exp = new RE( "^(\\d+\\.\\d+\\.\\d+\\.\\d+).*"); - if (!exp.match(net)) { + Matcher mm= exp.matcher(net); + if (!mm.matches()) { System.out.println("Bad broascat addr " + net); throw new IvyException("bad broadcast addr"); } - net=exp.getParen(1); - } catch ( RESyntaxException e ){ - System.out.println(e); - System.exit(0); + net=mm.group(1); + } catch ( PatternSyntaxException e ){ + e.printStackTrace(); + throw new RuntimeException(); } //System.out.println("next domain: "+net); return net; @@ -354,5 +363,17 @@ class IvyWatcher implements Runnable { private void traceDebug(String s){ if (debug) System.out.println("-->IvyWatcher["+myserial+","+bus.getSerial()+"]<-- "+s); } + + static { + try { + numbersPoint = Pattern.compile("([0-9]|\\.)+"); + recoucou = Pattern.compile("([0-9]+) ([0-9]+) ([^ ]*) (.*)",Pattern.DOTALL); + exp = Pattern.compile( "^(\\d+\\.\\d+\\.\\d+\\.\\d+).*"); + } catch (PatternSyntaxException res) { + res.printStackTrace(); + System.out.println("Regular Expression bug in Ivy source code ... bailing out"); + throw new RuntimeException(); + } + } } // class IvyWatcher diff --git a/src/Probe.java b/src/Probe.java index c3f05c2..4425dd4 100644 --- a/src/Probe.java +++ b/src/Probe.java @@ -7,8 +7,12 @@ * (c) CENA * * Changelog: + * 1.2.14 + * - uses the "new" for: loop construct of Java5 + * - throws RuntimeException instead of System.exit(), allows code reuse + * - switch from gnu regexp (deprecated) to the built in java regexp * 1.2.13 - * - adds support for RESyntaxException + * - adds support for RESyntaxException * 1.2.12 * - .ping is back * 1.2.9 @@ -59,7 +63,7 @@ import fr.dgac.ivy.* ; import java.io.*; import java.util.*; import gnu.getopt.Getopt; -import org.apache.regexp.*; +import java.util.regex.*; public class Probe implements IvyApplicationListener, IvyMessageListener, IvyBindListener, Runnable { @@ -110,14 +114,15 @@ public class Probe implements IvyApplicationListener, IvyMessageListener, IvyBin java.util.StringTokenizer classTok = new java.util.StringTokenizer(opt.getOptarg(),","); messageClass=new String[classTok.countTokens()]; System.out.println("YANNNN "+messageClass.length); - for (int i=0;classTok.hasMoreElements();) messageClass[i++]=new String((String)classTok.nextElement()); + for (int i=0;classTok.hasMoreElements();) + messageClass[i++] = classTok.nextToken(); break; case 'n': name=opt.getOptarg(); break; case 'q': quiet=true; break; case 't': timestamp=true; break; case 's': sendsToSelf=true; break; case 'h': - default: System.out.println(helpmsg); System.exit(0); + default: System.out.println(helpmsg); return; } Probe p = new Probe(new BufferedReader(new InputStreamReader(System.in)),timestamp,quiet,System.getProperty("IVY_DEBUG")!=null); p.setExitOnDie(true); @@ -126,7 +131,7 @@ public class Probe implements IvyApplicationListener, IvyMessageListener, IvyBin bus.sendToSelf(sendsToSelf); if (messageClass!=null) { System.out.println("using a message class filter of "+messageClass.length+" elements"); - for (int i=0;i<messageClass.length;i++) System.out.println(messageClass[i]); + for (String cls: messageClass) System.out.println(cls); bus.setFilter(messageClass); } for (int i=opt.getOptind();i<args.length;i++) { @@ -145,19 +150,17 @@ public class Probe implements IvyApplicationListener, IvyMessageListener, IvyBin private BufferedReader in; private volatile Thread looperThread; private Ivy bus; - private boolean timestamp,quiet,debug,exitOnDie=false; - - private static RE directMsgRE, timeCountRE; - + private boolean timestamp,quiet,debug,exitOnDie=false, encore=true; + private static Pattern directMsgRE, timeCountRE; static { try { - directMsgRE = new RE("^\\.direct ([^ ]*) ([0-9]+) (.*)"); - timeCountRE = new RE("^\\.time (\\d+) (.*)"); - } catch ( RESyntaxException res ) { + directMsgRE = Pattern.compile("^\\.direct ([^ ]*) ([0-9]+) (.*)"); + timeCountRE = Pattern.compile("^\\.time (\\d+) (.*)"); + } catch ( PatternSyntaxException res ) { res.printStackTrace(); System.out.println("Regular Expression bug in Ivy source code ... bailing out"); - System.exit(0); + throw new RuntimeException(); } } public Probe(BufferedReader in, boolean timestamp,boolean quiet,boolean debug) { @@ -181,13 +184,14 @@ public class Probe implements IvyApplicationListener, IvyMessageListener, IvyBin traceDebug("Probe Thread started"); Thread thisThread=Thread.currentThread(); String s; - println(bus.getSelfIvyClient().getApplicationName()+ " ready, type .help and return to get help"); + SelfIvyClient sic = bus.getSelfIvyClient(); + println(sic.getApplicationName()+ " ready, type .help and return to get help"); // "infinite" loop on keyboard input - while (looperThread==thisThread) { + while (encore && looperThread==thisThread) { try { s=in.readLine(); if (s==null) break; - parseCommand(s); + if (!parseCommand(s)) break; } catch (NullPointerException e) { // EOF triggered by a ^D, for instance break; @@ -201,35 +205,36 @@ public class Probe implements IvyApplicationListener, IvyMessageListener, IvyBin traceDebug("Probe Thread stopped"); } - void parseCommand(String s) throws IOException { + boolean parseCommand(String s) throws IOException { traceDebug("parsing the ["+s+"] (length "+s.length()+") string"); // crude parsing of the ".xyz" commands + Matcher m = directMsgRE.matcher(s); + Matcher mtime = timeCountRE.matcher(s); if (s.length()==0) { try { println("-> Sent to " +bus.sendMsg(s)+" peers"); } catch (IvyException ie) { println("-> not sent, the message contains incorrect characters"); } - } else if (directMsgRE.match(s)) { - String target = directMsgRE.getParen(1); - int id = Integer.parseInt(directMsgRE.getParen(2)); - String message = directMsgRE.getParen(3); - Vector v=bus.getIvyClientsByName(target); + } else if (m.matches()) { + String target = m.group(1); + int id = Integer.parseInt(m.group(2)); + String message = m.group(3); + Vector<IvyClient>v=bus.getIvyClientsByName(target); if (v.size()==0) println("no Ivy client with the name \""+target+"\""); try { - for (int i=0;i<v.size();i++) ((IvyClient)v.elementAt(i)).sendDirectMsg(id,message); + for (IvyClient ic : v) ic.sendDirectMsg(id,message); } catch (IvyException ie) { println("-> not sent, the message contains incorrect characters"); } - return; } else if (s.lastIndexOf(".dieall-yes-i-am-sure")>=0){ - Vector v=bus.getIvyClients(); - for (int i=0;i<v.size();i++) ((IvyClient)v.elementAt(i)).sendDie("java probe wants you to leave the bus"); + Vector<IvyClient>v=bus.getIvyClients(); + for (IvyClient ic: v) ic.sendDie("java probe wants you to leave the bus"); } else if (s.lastIndexOf(".die ")>=0){ String target=s.substring(5); - Vector v=bus.getIvyClientsByName(target); + Vector<IvyClient>v=bus.getIvyClientsByName(target); if (v.size()==0) println("no Ivy client with the name \""+target+"\""); - for (int i=0;i<v.size();i++) ((IvyClient)v.elementAt(i)).sendDie("java probe wants you to leave the bus"); + for (IvyClient ic: v) ic.sendDie("java probe wants you to leave the bus"); } else if (s.lastIndexOf(".unbind ")>=0){ String regexp=s.substring(8); if (bus.unBindMsg(regexp)) { @@ -238,14 +243,13 @@ public class Probe implements IvyApplicationListener, IvyMessageListener, IvyBin println("you can't unsubscribe to " + regexp + ", your're not subscribed to it"); } } else if (s.lastIndexOf(".bound *")>=0){ - Vector v=bus.getIvyClients(); + Vector<IvyClient>v=bus.getIvyClients(); int total=0; int boundedtotal=0; - for (int i=0;i<v.size();i++) { - IvyClient ic=(IvyClient)v.elementAt(i); - for (Enumeration e = ic.getRegexps();e.hasMoreElements();) { + for (IvyClient ic: v) { + for (Enumeration<String> e = ic.getRegexps();e.hasMoreElements();) { total++; - String r = (String)e.nextElement(); + String r = e.nextElement(); if (r.startsWith("^")) boundedtotal++; println(ic.getApplicationName()+" has subscribed to: "+r); } @@ -255,13 +259,12 @@ public class Probe implements IvyApplicationListener, IvyMessageListener, IvyBin int total=0; int boundedtotal=0; String target=s.substring(7); - Vector v=bus.getIvyClientsByName(target); + Vector<IvyClient>v=bus.getIvyClientsByName(target); if (v.size()==0) println("no Ivy client with the name \""+target+"\""); - for (int i=0;i<v.size();i++) { - IvyClient ic=(IvyClient)v.elementAt(i); - for (Enumeration e = ic.getRegexps();e.hasMoreElements();) { + for (IvyClient ic:v) { + for (Enumeration<String> e = ic.getRegexps();e.hasMoreElements();) { total++; - String r = (String)e.nextElement(); + String r = e.nextElement(); if (r.startsWith("^")) boundedtotal++; println(target+" has subscribed to: "+(String)e.nextElement()); } @@ -269,8 +272,8 @@ public class Probe implements IvyApplicationListener, IvyMessageListener, IvyBin } } else if (s.lastIndexOf(".bound")>=0){ println("you have subscribed to:"); - for (Enumeration e = bus.getSelfIvyClient().getRegexps();e.hasMoreElements();) { - println("\t"+(String)e.nextElement()); + for (Enumeration<String> e = bus.getSelfIvyClient().getRegexps();e.hasMoreElements();) { + println("\t"+e.nextElement()); } } else if (s.lastIndexOf(".bind ")>=0){ String regexp=s.substring(6); @@ -282,20 +285,18 @@ public class Probe implements IvyApplicationListener, IvyMessageListener, IvyBin } } else if ( (s.lastIndexOf(".quit")>=0)||(s.lastIndexOf(".bye")>=0)){ bus.stop(); - System.exit(0); + return false; } else if (s.lastIndexOf(".list")>=0) { - Vector v = bus.getIvyClients(); + Vector<IvyClient> v = bus.getIvyClients(); println(v.size()+" clients on the bus"); - for (int i=0;i<v.size();i++) { - println("-> "+((IvyClient)v.elementAt(i)).getApplicationName()); - } + for (IvyClient ic: v) println("-> "+ic.getApplicationName()); } else if ( s.lastIndexOf(".ping ")>=0) { String target=s.substring(6); - Vector v=bus.getIvyClientsByName(target); + Vector<IvyClient>v=bus.getIvyClientsByName(target); if (v.size()==0) println("no Ivy client with the name \""+target+"\""); - for (int i=0;i<v.size();i++) { + for (IvyClient ic:v) { try { - ((IvyClient)v.elementAt(i)).ping(new PingCallback() { + ic.ping(new PingCallback() { public void pongReceived(IvyClient ic,int elapsedTime){ System.out.println("round trip to "+ic.getApplicationName()+" "+elapsedTime+" ms"); } @@ -306,17 +307,15 @@ public class Probe implements IvyApplicationListener, IvyMessageListener, IvyBin } } else if ( s.lastIndexOf(".where ")>=0) { String target=s.substring(7); - Vector v=bus.getIvyClientsByName(target); + Vector<IvyClient>v=bus.getIvyClientsByName(target); if (v.size()==0) println("no Ivy client with the name \""+target+"\""); - for (int i=0;i<v.size();i++) { - println(target+" runs on "+((IvyClient)v.elementAt(i)).getHostName()); - } - } else if (timeCountRE.match(s)) { + for (IvyClient ic: v) println(target+" runs on "+ic.getHostName()); + } else if (mtime.matches()) { long before = new java.util.Date().getTime(); - int times=Integer.parseInt(timeCountRE.getParen(1)); + int times=Integer.parseInt(mtime.group(1)); try { int n=0; - for (int i=0;i<times;i++) n=bus.sendMsg(timeCountRE.getParen(2)); + for (int i=0;i<times;i++) n=bus.sendMsg(mtime.group(2)); long after = new java.util.Date().getTime(); println("-> it took "+(after-before)+"ms to send "+times+" to " +n+" peers"); } catch (IvyException ie) { @@ -334,11 +333,12 @@ public class Probe implements IvyApplicationListener, IvyMessageListener, IvyBin println("-> not sent, the line contains incorrect characters"); } } + return true; } // parseCommand public void bindPerformed(IvyClient client,int id,String re) { String s=""; - if (!bus.CheckRegexp(re)) s=" WITH NO EFFECT"; + if (!bus.checkRegexp(re)) s=" WITH NO EFFECT, because of the filters"; println(client.getApplicationName() + " subscribes to " +re+s); } @@ -358,7 +358,7 @@ public class Probe implements IvyApplicationListener, IvyMessageListener, IvyBin public void die(IvyClient client, int id,String msgarg) { println("received die msg from " + client.getApplicationName() +" with the message: "+msgarg+", good bye"); - /* I cannot stop the readLine(), because it is native code */ + /* I cannot stop the readLine(), because it is native code ?! */ if (exitOnDie) System.exit(0); } @@ -367,9 +367,14 @@ public class Probe implements IvyApplicationListener, IvyMessageListener, IvyBin } public void receive(IvyClient client, String[] args) { - String s=client.getApplicationName() + " sent"; - for (int i=0;i<args.length;i++) s+=" '"+args[i]+"'"; - println(s); + StringBuffer s=new StringBuffer(client.getApplicationName()); + s.append(" sent"); + for (String arg: args) { + s.append(" '"); + s.append(arg); + s.append("'"); + } + println(s.toString()); } private void traceDebug(String s){ if (debug) System.out.println("-->Probe<-- "+s); } diff --git a/src/ProxyClient.java b/src/ProxyClient.java index 91af0ba..431cfc5 100644 --- a/src/ProxyClient.java +++ b/src/ProxyClient.java @@ -7,6 +7,13 @@ * (c) ENAC * * changelog: + * 1.2.14 + * - remove the Thread.start() from the constructor, to avoid mulithread issues + * see * http://findbugs.sourceforge.net/bugDescriptions.html#SC_START_IN_CTOR + * now ,we have to call IvyClient.start() after it has been created + * - throws RuntimeException instead of System.exit(), allows code reuse + * - switch from gnu regexp (deprecated) to the built in java regexp + * - add generic types to declarations * 1.2.13 * - adds support for RESyntaxException * @@ -16,22 +23,21 @@ import java.io.*; import java.net.*; import java.util.* ; import gnu.getopt.Getopt; -import org.apache.regexp.*; +import java.util.regex.*; public class ProxyClient extends Ivy { private Socket clientSocket; private PrintWriter out; private BufferedReader in; - private boolean isRunning=false; private static boolean debug = (System.getProperty("IVY_DEBUG")!=null) ; private volatile Thread clientThread; // volatile to ensure the quick communication - private Hashtable id=new Hashtable(); - private Hashtable ghosts = new Hashtable(); - private Hashtable puppets =new Hashtable(); // key=id value=Puppet + private Hashtable<String,String> id=new Hashtable<String,String>(); + private Hashtable<String,Ghost>ghosts = new Hashtable<String,Ghost>(); + private Hashtable<String,Puppet> puppets =new Hashtable<String,Puppet>(); // key=id value=Puppet String domain=null; - public static int DEFAULT_SERVICE_PORT = 3456 ; + public static final int DEFAULT_SERVICE_PORT = 3456 ; public static final String DEFAULTNAME = "ProxyClient"; public static final String helpmsg = "usage: java fr.dgac.ivy.ProxyClient [options] hostname\n\t-b BUS\tspecifies the Ivy bus domain\n\t-p\tport number, default "+DEFAULT_SERVICE_PORT+"\n\t-n ivyname (default "+DEFAULTNAME+")\n\t-q\tquiet, no tty output\n\t-d\tdebug\n\t-h\thelp\ncontacts the ProxyMaster on hostname\n"; @@ -63,25 +69,25 @@ public class ProxyClient extends Ivy { servicePort = Integer.parseInt(s=opt.getOptarg()); } catch (NumberFormatException nfe) { System.out.println("Invalid port number: " + s ); - System.exit(0); + throw new RuntimeException(); } break; case 'h': default: System.out.println(helpmsg); - System.exit(0); + return ; } String hostname="localhost"; try { - ProxyClient pc = new ProxyClient(hostname,servicePort,domain); + new ProxyClient(hostname,servicePort,domain).start(); } catch (IvyException ie) { System.out.println("error, can't connect to Ivy"); ie.printStackTrace(); - System.exit(-1); + throw new RuntimeException(); } catch (IOException ioe) { System.out.println("error, can't connect to the proxy master"); ioe.printStackTrace(); - System.exit(-1); + throw new RuntimeException(); } } @@ -92,52 +98,54 @@ public class ProxyClient extends Ivy { in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); out = new PrintWriter(new OutputStreamWriter(clientSocket.getOutputStream())); clientThread=new Thread(new Servicer()); // - clientThread.start(); this.domain=domain; + } + + protected void start() throws IvyException { + clientThread.start(); start(domain); send("Hello bus="+domain); } - static RE getId,fwdGhost,fwdPuppet,puppetRe; + static Pattern getId,fwdGhost,fwdPuppet,puppetRe; static { try { - getId=new RE("^ID id=(.*) value=(.*)"); - fwdGhost=new RE("^ForwardGhost id=(.*) buffer=(.*)"); - fwdPuppet=new RE("^ForwardPuppet id=(.*) buffer=(.*)"); - puppetRe=new RE("^CreatePuppet id=(.*)"); - } catch ( RESyntaxException res ) { + getId=Pattern.compile("^ID id=(.*) value=(.*)"); + fwdGhost=Pattern.compile("^ForwardGhost id=(.*) buffer=(.*)"); + fwdPuppet=Pattern.compile("^ForwardPuppet id=(.*) buffer=(.*)"); + puppetRe=Pattern.compile("^CreatePuppet id=(.*)"); + } catch ( PatternSyntaxException res ) { res.printStackTrace(); System.out.println("Regular Expression bug in Ivy source code ... bailing out"); - System.exit(0); + throw new RuntimeException(); } } void parseMsg(String msg) { // System.out.println("PC parsing "+msg); - if (getId.match(msg)) { - id.put(getId.getParen(1),getId.getParen(2)); - } else if (puppetRe.match(msg)) { // I must create a puppet - String puppetId = puppetRe.getParen(1); + Matcher m; + if ((m=getId.matcher(msg)).matches()) { + id.put(m.group(1),m.group(2)); + } else if ((m=puppetRe.matcher(msg)).matches()) { // I must create a puppet + String puppetId = m.group(1); puppets.put(puppetId,new Puppet(this,puppetId,domain)); - } else if (fwdGhost.match(msg)) { // I must forward to the ghost - Ghost g = (Ghost)ghosts.get(fwdGhost.getParen(1)); - try { g.sendBuffer(fwdGhost.getParen(2)); } catch( IvyException ie) { ie.printStackTrace(); } - } else if (fwdPuppet.match(msg)) { // I must forward to the puppet - Puppet p = (Puppet)puppets.get(fwdPuppet.getParen(1)); - try { p.parse(fwdPuppet.getParen(2)); } catch( IvyException ie) { ie.printStackTrace(); } + } else if ((m=fwdGhost.matcher(msg)).matches()) { // I must forward to the ghost + Ghost g = ghosts.get(m.group(1)); + try { g.sendBuffer(m.group(2)); } catch( IvyException ie) { ie.printStackTrace(); } + } else if ((m=fwdPuppet.matcher(msg)).matches()) { // I must forward to the puppet + Puppet p = puppets.get(m.group(1)); + try { p.parse(m.group(2)); } catch( IvyException ie) { ie.printStackTrace(); } } else { System.out.println("unknown message "+msg); } } - void removePuppet(Puppet p){puppets.remove(p);} - class Servicer implements Runnable { public void run() { - Thread thisThread = Thread.currentThread(); + //Thread thisThread = Thread.currentThread(); traceDebug("Thread started"); - String msg = null; + String msg; try { while (true) { msg=in.readLine(); @@ -147,11 +155,11 @@ public class ProxyClient extends Ivy { } catch (IOException ioe) { traceDebug("Subreader exception ..."); ioe.printStackTrace(); - System.exit(0); + throw new RuntimeException(); } traceDebug("Subreader Thread stopped"); System.out.println("connexion to ProxyMaster lost"); - for (Enumeration e=puppets.elements();e.hasMoreElements();) ((Puppet)e.nextElement()).stop(); + for (Puppet p:puppets.values()) p.stop(); stop(); // leave the bus TODO: make a disconnexion/reconnexion possible ? } } @@ -167,27 +175,29 @@ public class ProxyClient extends Ivy { * TODO: remember everything in case a new proxy client comes ? */ protected IvyClient createIvyClient(Socket s,int port, boolean domachin) throws IOException { - // TODO si c'est un puppet, je ne dois pas créer de Ghost - // voir même me déconnecter du biniou ? - for (Enumeration e=puppets.elements();e.hasMoreElements();) { - if (( ((Puppet)e.nextElement()).bus.getAP() == port ) && !domachin ) { + IvyClient i; + // TODO si c'est un puppet, je ne dois pas creer de Ghost + // voir meme me deconnecter du biniou ? + for (Puppet p:puppets.values()) { + if (( p.bus.getAP() == port ) && !domachin ) { // this new Ivy agent is in fact one of my puppets ... System.out.println("not Ghosting this (probable) Puppet Ivy agent"); - return new IvyClient(this,s,port,domachin); + i= new IvyClient(this,s,port,domachin); + i.start(); + return i; } } String key = getWBUId(); String ghostId; send("GetID id="+key); // asks a centralized ID from ProxyMaster try { // waits for the answer - while ((ghostId=(String)id.get(key))==null) { Thread.sleep(200); } + while ((ghostId=id.get(key))==null) { Thread.sleep(200); } Ghost g = new Ghost(this,s,port,domachin,ghostId,this); ghosts.put(ghostId,g); return g; } catch (InterruptedException ie) { ie.printStackTrace(); } System.out.println("error waiting"); - System.exit(0); - return null; + throw new RuntimeException(); } /* diff --git a/src/ProxyMaster.java b/src/ProxyMaster.java index f8585fd..65d8c7f 100644 --- a/src/ProxyMaster.java +++ b/src/ProxyMaster.java @@ -7,6 +7,10 @@ * (c) ENAC * * changelog: + * 1.2.14 + * - throws RuntimeException instead of System.exit(), allows code reuse + * - switch from gnu regexp (deprecated) to the built in java regexp + * - add generic types to declarations * 1.2.13 * - adds support for RESyntaxException * 1.2.12 @@ -17,23 +21,37 @@ import java.io.*; import java.net.*; import java.util.* ; import gnu.getopt.Getopt; -import org.apache.regexp.*; +import java.util.regex.*; public class ProxyMaster { private ServerSocket serviceSocket; - private boolean isRunning=false; private static boolean debug=false; private boolean doRun=true; // stops running when set to false - private Vector proxyClients = new Vector(); - private Hashtable ghostFathers = new Hashtable(); // key: ghostId value: SubReader + private Vector<SubReader> proxyClients = new Vector<SubReader>(); + private Hashtable<String,SubReader> ghostFathers = new Hashtable<String,SubReader>(); // key: ghostId value: SubReader private static int serial=0; - public static int DEFAULT_SERVICE_PORT = 3456 ; + public static final int DEFAULT_SERVICE_PORT = 3456 ; public static final String DEFAULTNAME = "ProxyMaster"; public static final String helpmsg = "usage: java fr.dgac.ivy.ProxyMaster [options]\n\t-p\tport number, default "+DEFAULT_SERVICE_PORT+"\n\t-q\tquiet, no tty output\n\t-d\tdebug\n\t-h\thelp\nListens on the TCP port for ProxyClients to join.\n"; + static Pattern helloRE, getId,fwdPuppet,fwdGhost; private static String name = DEFAULTNAME; + + static { + try { + helloRE=Pattern.compile("^Hello bus=(.*)"); + getId=Pattern.compile("^GetID id=(.*)"); + fwdPuppet=Pattern.compile("^ForwardPuppet id=(.*) buffer=(.*)"); + fwdGhost=Pattern.compile("^ForwardGhost id=(.*) buffer=(.*)"); + } catch ( PatternSyntaxException res ) { + res.printStackTrace(); + System.out.println("Regular Expression bug in Ivy source code ... bailing out"); + throw new RuntimeException(); + } + } + public static void main(String[] args) { Ivy bus; Getopt opt = new Getopt("ProxyMaster",args,"dqp:h"); @@ -54,21 +72,21 @@ public class ProxyMaster { servicePort = Integer.parseInt(s=opt.getOptarg()); } catch (NumberFormatException nfe) { System.out.println("Invalid port number: " + s ); - System.exit(0); + throw new RuntimeException(); } break; case 'h': default: System.out.println(helpmsg); - System.exit(0); + return; } try { if (!quiet) System.out.println("listening on "+servicePort); - ProxyMaster pm = new ProxyMaster(servicePort); + new ProxyMaster(servicePort); } catch (IOException ioe) { System.out.println("error, can't set up the proxy master"); ioe.printStackTrace(); - System.exit(-1); + return; } } @@ -88,19 +106,8 @@ public class ProxyMaster { PrintWriter out; String hostname=null; // I will know from the socket String busDomain=null; // I will know it from the Hello message - RE helloRE, getId,fwdPuppet,fwdGhost; SubReader(Socket socket) throws IOException { - try { - helloRE=new RE("^Hello bus=(.*)"); - getId=new RE("^GetID id=(.*)"); - fwdPuppet=new RE("^ForwardPuppet id=(.*) buffer=(.*)"); - fwdGhost=new RE("^ForwardGhost id=(.*) buffer=(.*)"); - } catch ( RESyntaxException res ) { - res.printStackTrace(); - System.out.println("Regular Expression bug in Ivy source code ... bailing out"); - System.exit(0); - } proxyClients.addElement(this); hostname = socket.getInetAddress().getHostName(); in=new BufferedReader(new InputStreamReader(socket.getInputStream())); @@ -125,7 +132,7 @@ public class ProxyMaster { } catch (IOException ioe) { traceDebug("Subreader exception ..."); ioe.printStackTrace(); - System.exit(0); + throw new RuntimeException(); } traceDebug("Subreader Thread stopped"); System.out.println("ProxyClient on "+hostname+", bus "+busDomain+" disconnected"); @@ -136,19 +143,19 @@ public class ProxyMaster { void parseMsg(String msg) { // System.out.println("PM parsing "+msg); - if (helloRE.match(msg)) { - busDomain = helloRE.getParen(1); + Matcher m; + if ((m=helloRE.matcher(msg)).matches()) { + busDomain = m.group(1); System.out.println("PC connected from "+hostname+", on the bus "+busDomain); - } else if (getId.match(msg)) { + } else if ((m=getId.matcher(msg)).matches()) { // a new Ghost has appeared and requests an Id System.out.println("PM registering a new Ghost"); - String newGhostId = new Integer(serial++).toString(); + String newGhostId = Integer.valueOf(serial++).toString(); // I give it its ID - send("ID id="+getId.getParen(1)+" value="+newGhostId); + send("ID id="+m.group(1)+" value="+newGhostId); ghostFathers.put(newGhostId,this); // remember the SubReader holding this Ghost // I ask all other Clients to prepare a puppet - for (Enumeration e=proxyClients.elements();e.hasMoreElements();) { - SubReader sr = (SubReader)e.nextElement(); + for (SubReader sr : proxyClients) { if (sr!=SubReader.this) { // System.out.println("propagate CreatePuppet to "+sr.busDomain); sr.send("CreatePuppet id="+newGhostId); @@ -156,14 +163,14 @@ public class ProxyMaster { // System.out.println("won't propagate CreatePuppet to "+sr.busDomain); } } - } else if (fwdGhost.match(msg)) { + } else if ((m=fwdGhost.matcher(msg)).matches()) { System.out.println("PM forwarding ["+msg+"] to its Ghost"); - SubReader sr = (SubReader) ghostFathers.get(fwdGhost.getParen(1)); + SubReader sr = ghostFathers.get(m.group(1)); sr.send(msg); - } else if (fwdPuppet.match(msg)) { + } else if ((m=fwdPuppet.matcher(msg)).matches()) { System.out.println("PM forwarding ["+msg+"] to all other PCs"); - for (Enumeration e=proxyClients.elements();e.hasMoreElements();) { - SubReader sr = (SubReader)e.nextElement(); + for (Enumeration<SubReader>e=proxyClients.elements();e.hasMoreElements();) { + SubReader sr = e.nextElement(); if (sr!=SubReader.this) sr.send(msg); } } else { diff --git a/src/Puppet.java b/src/Puppet.java index 5398798..17844ea 100644 --- a/src/Puppet.java +++ b/src/Puppet.java @@ -7,6 +7,9 @@ * (c) CENA 1998-2004 * * CHANGELOG: + * 1.2.14 + * - switch from gnu regexp (deprecated) to the built in java regexp + * - add generic types to declarations * 1.2.13: * - adds support for RESyntaxException */ @@ -16,13 +19,13 @@ import java.lang.Thread; import java.net.*; import java.io.*; import java.util.*; -import org.apache.regexp.*; +import java.util.regex.*; class Puppet { // the mapping between Ghost regexp and local bus regexp numbers - Hashtable bound = new Hashtable(); // ghostID localID - Hashtable regexps = new Hashtable(); // ghostID textRegexp + Hashtable<String,String> bound = new Hashtable<String,String>(); // ghostID localID + Hashtable<String,String>regexps = new Hashtable<String,String>(); // ghostID textRegexp String domain; String appName; ProxyClient pc; @@ -42,13 +45,19 @@ class Puppet { String localId,ghostId; public ForwardMessenger(String ghostId,String re) throws IvyException { this.ghostId=ghostId; - this.localId = (new Integer(bus.bindMsg(re,ForwardMessenger.this))).toString(); + this.localId = Integer.valueOf(bus.bindMsg(re,ForwardMessenger.this)).toString(); bound.put(ghostId,localId); } public void receive(IvyClient ic,String args[]) { - String tosend = IvyClient.Msg+" "+ghostId+IvyClient.StartArg; - for (int i=0;i<args.length;i++) tosend+=args[i]+IvyClient.EndArg; - sendGhost(tosend); + StringBuffer tosend = new StringBuffer(IvyClient.Msg); + tosend.append(" "); + tosend.append(ghostId); + tosend.append(IvyClient.StartArg); + for (int i=0;i<args.length;i++) { + tosend.append(args[i]); + tosend.append(IvyClient.EndArg); + } + sendGhost(tosend.toString()); } } // ForwardMessenger @@ -70,12 +79,12 @@ class Puppet { } // ivy forwarded protocol message - static RE ivyProto; + static Pattern ivyProto; static { try { - ivyProto = new RE("(\\d+) (\\d+)\\02(.*)"); - } catch ( RESyntaxException res ) { + ivyProto = Pattern.compile("(\\d+) (\\d+)\\02(.*)"); + } catch (PatternSyntaxException res ) { res.printStackTrace(); System.out.println("Regular Expression bug in Ivy source code ... bailing out"); System.exit(0); @@ -83,10 +92,11 @@ class Puppet { } void parse(String s) throws IvyException { - if (!ivyProto.match(s)) { System.out.println("Puppet error, can't parse "+s); return; } - int pcode=Integer.parseInt(ivyProto.getParen(1)); - String pid=ivyProto.getParen(2); - String args=ivyProto.getParen(3); + Matcher m; + if (!(m=ivyProto.matcher(s)).matches()) { System.out.println("Puppet error, can't parse "+s); return; } + int pcode=Integer.parseInt(m.group(1)); + String pid=m.group(2); + String args=m.group(3); trace("must parse code:"+pcode+" id:"+pid+" args:"+args); switch (pcode) { case IvyClient.AddRegexp: // the Ghost's peer subscribes to something @@ -105,16 +115,12 @@ class Puppet { case IvyClient.Msg: // the Ghost's peer sends a message to ProxyClient, with regard to one // of our subscriptions - // TODO à qui le faire passer ? + // TODO a qui le faire passer ? break; case IvyClient.SchizoToken: appName = args; bus = new PuppetIvy(appName,appName+" fakeready",null); - for (Enumeration e = regexps.keys();e.hasMoreElements();) { - String ghostId=(String)e.nextElement(); - String re=(String)regexps.get(ghostId); - new ForwardMessenger(ghostId,re); - } + for ( String ghostId: regexps.keySet() )new ForwardMessenger(ghostId,regexps.get(ghostId)); started=true; trace("starting the bus on "+domain); bus.start(domain); @@ -129,15 +135,15 @@ class Puppet { } } - class PuppetIvy extends Ivy { + static class PuppetIvy extends Ivy { PuppetIvy(String name,String ready,IvyApplicationListener ial){super(name,ready,ial);} protected IvyClient createIvyClient(Socket s,int port, boolean domachin) throws IOException { return new PuppetIvyClient(PuppetIvy.this,s,port,domachin); } - int getAP() {return applicationPort;} + int getAP() {return getAppPort();} } - class PuppetIvyClient extends IvyClient { + static class PuppetIvyClient extends IvyClient { PuppetIvyClient(Ivy bus,Socket s,int port,boolean b) throws IOException { super(bus,s,port,b); } protected synchronized void sendBuffer( String s ) throws IvyException { super.sendBuffer(s); // and to all the agents on the Ghost bus ? I'm not sure diff --git a/src/SelfIvyClient.java b/src/SelfIvyClient.java index f4a1cd2..a87c859 100644 --- a/src/SelfIvyClient.java +++ b/src/SelfIvyClient.java @@ -6,6 +6,10 @@ * @since 1.2.4 * * CHANGELOG: + * 1.2.14 + * - uses autoboxing for Boolean + * - switch from gnu regexp (deprecated) to the built in java regexp + * - add generic types to declarations * 1.2.7: * - fixes a bug on unbindMsg(String) ( closes Matthieu's burreport ) * 1.2.6: @@ -19,15 +23,14 @@ package fr.dgac.ivy ; import java.util.*; -import org.apache.regexp.*; +import java.util.regex.*; -class SelfIvyClient extends IvyClient { +public class SelfIvyClient extends IvyClient { private Ivy bus; private static int serial=0; /* an unique ID for each regexp */ - private Hashtable callbacks=new Hashtable(); - private Hashtable threadedFlag=new Hashtable(); - private boolean massThreaded=false; + private Hashtable<Integer,IvyMessageListener> callbacks=new Hashtable<Integer,IvyMessageListener>(); + private Hashtable<Integer,BindType> threadedFlag=new Hashtable<Integer,BindType>(); public void sendDirectMsg(int id,String message) { bus.directMessage(this,id,message); @@ -40,23 +43,23 @@ class SelfIvyClient extends IvyClient { this.appName=appName; } - synchronized protected int bindMsg(String sregexp, IvyMessageListener callback, boolean threaded ) throws IvyException { + synchronized protected int bindMsg(String sregexp, IvyMessageListener callback, BindType type ) throws IvyException { // creates a new binding (regexp,callback) try { - RE re=new RE(sregexp); - Integer key = new Integer(serial++); + Pattern re=Pattern.compile(sregexp,Pattern.DOTALL); + Integer key = serial++; regexps.put(key,re); regexpsText.put(key,sregexp); callbacks.put(key,callback); - threadedFlag.put(key,new Boolean(threaded)); + threadedFlag.put(key,type); // use autoboxing of boolean return key.intValue(); - } catch (RESyntaxException ree) { + } catch (PatternSyntaxException ree) { throw new IvyException("Invalid regexp " + sregexp); } } synchronized protected void unBindMsg(int id) throws IvyException { - Integer key = new Integer(id); + Integer key = id; if ( ( regexps.remove(key) == null ) || (regexpsText.remove(key) == null ) || (callbacks.remove(key) == null ) @@ -68,9 +71,8 @@ class SelfIvyClient extends IvyClient { // unbinds to the first regexp synchronized protected boolean unBindMsg(String re) { if (!regexpsText.contains(re)) return false; - for (Enumeration e=regexpsText.keys();e.hasMoreElements();) { - Integer k = (Integer)e.nextElement(); - if ( ((String)regexpsText.get(k)).compareTo(re) == 0) { + for (Integer k : regexpsText.keySet()) { + if ( (regexpsText.get(k)).compareTo(re) == 0) { try { bus.unBindMsg(k.intValue()); } catch (IvyException ie) { @@ -84,51 +86,80 @@ class SelfIvyClient extends IvyClient { protected int sendSelfMsg(String message) { int count = 0; - for (Enumeration e = regexps.keys();e.hasMoreElements();) { - Integer key = (Integer)e.nextElement(); - RE regexp = (RE)regexps.get(key); - String sre = (String)regexpsText.get(key); + traceDebug("trying to send to self the message <"+message+">"); + for (Integer key : regexps.keySet() ) { + Pattern regexp = regexps.get(key); + String sre = regexpsText.get(key); synchronized(regexp) { - if (!regexp.match(message)) continue; + traceDebug("checking against: "+sre); + Matcher m = regexp.matcher(message); + if (!m.matches()) { + traceDebug("checking against: "+sre+" failed"); + continue; + } + traceDebug("checking against: "+sre+" succeeded"); count++; - callCallback(this,key,toArgs(regexp)); + callCallback(this,key,toArgs(m)); } } return count; } protected void callCallback(IvyClient client, Integer key, String[] tab) { - IvyMessageListener callback=(IvyMessageListener)callbacks.get(key); + IvyMessageListener callback=callbacks.get(key); if (callback==null) { traceDebug("Not regexp matching id "+key.intValue()+", it must have been unsubscribed concurrently"); return; - // TODO check that nasty synchro issue, test suite: Request - } - Boolean b = (Boolean)threadedFlag.get(key); - if (callback==null) { - System.out.println("threadedFlag.get returns null for"+key.intValue()+", it must have been unsubscribed concurrently"); - return; + // DONE check that nasty synchro issue, test suite: Request } - boolean threaded=b.booleanValue(); - if (!threaded) { - // runs the callback in the same thread - callback.receive(client, tab); // TODO tab can be faulty ?! - } else { - // starts a new Thread for each callback ... ( Async API ) - new Runner(callback,client,tab); + BindType type = threadedFlag.get(key); + switch (type) { + case NORMAL: + // runs the callback in the same thread + callback.receive(client, tab); // can tab can be faulty ?! TODO + break; + case ASYNC: + // starts a new Thread for each callback ... ( Async API ) + new Runner(callback,client,tab); + break; + case SWING: + // deferes the callback to the Event Dispatch Thread + new SwingRunner(callback,client,tab); + break; } } - private String[] toArgs(RE re) { - String[] args = new String[re.getParenCount()-1]; - for(int sub=1;sub<re.getParenCount();sub++) { - args[sub-1]=re.getParen(sub); - if (bus.doProtectNewlines) args[sub-1]=decode(args[sub-1]); + private String[] toArgs(Matcher m) { + String[] args= + (m.groupCount()>0) ? new String[m.groupCount()] : new String[0]; + //System.out.println("DEBUG "+args.length+" arguments"); + for(int sub=0;sub<m.groupCount();sub++) { + args[sub]=m.group(sub+1); + if (bus.getProtectNewlines()) args[sub]=decode(args[sub]); + //System.out.println("DEBUG argument "+(sub)+"="+args[sub]); } return args; } - public String toString() { return "IvyClient (ourself)"+bus.appName+":"+appName; } + public String toString() { + return "IvyClient (ourself)"+bus.getAppName()+":"+appName; + } + + // a class to perform the execution of each new callback within the Event + // Dispatch Thread + // this is an experimental feature introduced in 1.2.14 + static class SwingRunner implements Runnable { + IvyMessageListener cb; + IvyClient c; + String[] args; + public SwingRunner(IvyMessageListener cb,IvyClient c,String[] a) { + this.cb=cb; + this.c=c; + args=a; + javax.swing.SwingUtilities.invokeLater(SwingRunner.this); + } + public void run() { cb.receive(c,args); } + } // a class to perform the threaded execution of each new message // this is an experimental feature introduced in 1.2.4 @@ -151,7 +182,7 @@ class SelfIvyClient extends IvyClient { private void traceDebug(String s){ if (debug) - System.out.println("-->SelfIvyClient "+bus.appName+":"+appName+"<-- "+s); + System.out.println("-->SelfIvyClient "+bus.getAppName()+":"+appName+"<-- "+s); } } diff --git a/src/WaiterClient.java b/src/WaiterClient.java index 34fd188..33196f2 100644 --- a/src/WaiterClient.java +++ b/src/WaiterClient.java @@ -17,9 +17,9 @@ class WaiterClient extends IvyApplicationAdapter implements Runnable { private boolean forever=false; private Thread t; String name; - Hashtable clients; + private Hashtable <Integer,IvyClient>clients ; - WaiterClient(String n,int timeout,Hashtable clients) { + WaiterClient(String n,int timeout,Hashtable <Integer,IvyClient>clients) { this.timeout=timeout; this.clients=clients; name=n; |