From 750f33265d208df8218f85359e3f027900c58363 Mon Sep 17 00:00:00 2001 From: jestin Date: Fri, 22 Jul 2011 16:49:57 +0000 Subject: Passage en 1.2.14 --- src/After.java | 10 +- src/Ghost.java | 1 - src/Ivy.java | 811 ++++++++++++++++++++++------------------ src/IvyApplicationAdapter.java | 2 +- src/IvyApplicationListener.java | 2 +- src/IvyClient.java | 178 +++++---- src/IvyDaemon.java | 23 +- src/IvyException.java | 2 +- src/IvyHttpGatewayClient.java | 1 - src/IvyHttpGatewayServlet.java | 24 +- src/IvyHttpGatewayTest.java | 1 - src/IvyMessageListener.java | 2 +- src/IvyWatcher.java | 113 +++--- src/Probe.java | 125 ++++--- src/ProxyClient.java | 96 ++--- src/ProxyMaster.java | 73 ++-- src/Puppet.java | 52 +-- src/SelfIvyClient.java | 113 ++++-- src/WaiterClient.java | 4 +- 19 files changed, 908 insertions(+), 725 deletions(-) (limited to 'src') 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 http://www.tls.cena.fr/products/ivy/ + * @author Yannick Jestin yannick.jestin&enac.fr + * @author http://www.tls.cena.fr/products/ivy/ * * (c) CENA 1998-2004 + * (c) ENAC 2005-2011 * *
  *Ivy bus = new Ivy("Dummy agent","ready",null);
@@ -13,6 +15,37 @@
  *
* * 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 watchers = new Vector(); 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 clients = new Hashtable(); + private Hashtable half = new Hashtable(); + private Vector ivyApplicationListenerList = new Vector(); + private Vector ivyBindListenerList = new Vector(); + private Vector sendThreads = new Vector(); 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 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 { *
  • One thread (IvyWatcher) for each traffic rendezvous (either UDP broadcast or TCPMulticast) *
  • One thread (serverThread/Ivy) to accept incoming connexions on server socket *
  • 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=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 getIvyClients() { + Vector v = new Vector(); + 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 getIvyClientsByName(final String name) { + Vector v = new Vector(); 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;index0 ){ + 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 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 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 http://www.tls.cena.fr/products/ivy/ * 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 http://www.tls.cena.fr/products/ivy/ * 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 PingCallbacksTable = new Hashtable(); - 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 regexps = new Hashtable(); + Hashtable regexpsText = new Hashtable(); 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 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 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 (Enumeratione=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;i15) ? 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;iv = new Vector(); while (toIvyClient["+clientKey+","+serial+"] "+app+" (remote "+appName+")<-- "+s); } private void traceDebug(String[] tab){ - String s = " string array " + tab.length + " elements: "; - for (int i=0;iMAXPONGCALLBACKS) { // 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(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 http://www.tls.cena.fr/products/ivy/ * 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 http://www.gnu.org/copyleft/lesser.html * - * @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 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(); } @@ -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 http://www.tls.cena.fr/products/ivy/ * 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 http://www.tls.cena.fr/products/ivy/ * * (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 alreadySocks=new Hashtable(); 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 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); + Vectorv=bus.getIvyClientsByName(target); if (v.size()==0) println("no Ivy client with the name \""+target+"\""); try { - for (int i=0;i 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;iv=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); + Vectorv=bus.getIvyClientsByName(target); if (v.size()==0) println("no Ivy client with the name \""+target+"\""); - for (int i=0;i=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(); + Vectorv=bus.getIvyClients(); int total=0; int boundedtotal=0; - for (int i=0;i 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); + Vectorv=bus.getIvyClientsByName(target); if (v.size()==0) println("no Ivy client with the name \""+target+"\""); - for (int i=0;i 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 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 v = bus.getIvyClients(); println(v.size()+" clients on the bus"); - for (int i=0;i "+((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); + Vectorv=bus.getIvyClientsByName(target); if (v.size()==0) println("no Ivy client with the name \""+target+"\""); - for (int i=0;i=0) { String target=s.substring(7); - Vector v=bus.getIvyClientsByName(target); + Vectorv=bus.getIvyClientsByName(target); if (v.size()==0) println("no Ivy client with the name \""+target+"\""); - for (int i=0;i 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;iProbe<-- "+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 id=new Hashtable(); + private Hashtableghosts = new Hashtable(); + private Hashtable puppets =new Hashtable(); // 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 proxyClients = new Vector(); + private Hashtable ghostFathers = new Hashtable(); // 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 (Enumeratione=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 bound = new Hashtable(); // ghostID localID + Hashtableregexps = new Hashtable(); // 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 callbacks=new Hashtable(); + private Hashtable threadedFlag=new Hashtable(); 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;sub0) ? new String[m.groupCount()] : new String[0]; + //System.out.println("DEBUG "+args.length+" arguments"); + for(int sub=0;subSelfIvyClient "+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 clients ; - WaiterClient(String n,int timeout,Hashtable clients) { + WaiterClient(String n,int timeout,Hashtable clients) { this.timeout=timeout; this.clients=clients; name=n; -- cgit v1.1