aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xsrc/Ivy.java362
-rwxr-xr-xsrc/IvyApplicationAdapter.java24
-rwxr-xr-xsrc/IvyApplicationListener.java41
-rwxr-xr-xsrc/IvyClient.java396
-rwxr-xr-xsrc/IvyException.java11
-rwxr-xr-xsrc/IvyMessageListener.java14
-rwxr-xr-xsrc/IvyWatcher.java278
-rw-r--r--src/Probe.java77
-rwxr-xr-xsrc/TestIvy.java115
-rw-r--r--src/TestIvySwing.java120
10 files changed, 860 insertions, 578 deletions
diff --git a/src/Ivy.java b/src/Ivy.java
index 8756c94..5b423eb 100755
--- a/src/Ivy.java
+++ b/src/Ivy.java
@@ -1,8 +1,11 @@
-//
-// API de connexion au bus logiciel ivy
-//
-//
-
+/**
+ * a software bus package
+ *
+ * @author François-Régis Colin
+ * @author Yannick Jestin
+ * @author <a href="http://www.tls.cena.fr/products/ivy/">http://www.tls.cena.fr/products/ivy/</a>
+ *
+ */
package fr.dgac.ivy ;
import java.net.*;
@@ -11,179 +14,270 @@ import java.util.Vector;
import java.util.Hashtable;
import java.util.StringTokenizer;
+/**
+ * A class connecting to the Ivy software bus.
+ * For example:
+ *<pre>
+ *Ivy bus = new Ivy("Dummy agent","ready",null);
+ *bus.bindMsg("(.*)",myMessageListener);
+ *bus.start(null);
+ *</pre>
+ */
public class Ivy implements Runnable, IvyApplicationListener {
- static private boolean debug = (System.getProperty("IVY_DEBUG")!=null) ;
-
+ /**
+ * the name of the application on the bus
+ */
+ public String appName;
+ /**
+ * the protocol version number
+ */
+ public static final int PROCOCOLVERSION = 3 ;
+ /**
+ * the port for the UDP rendez vous, if none is supplied
+ */
public static final int DEFAULT_PORT = 2010 ;
+ /**
+ * the domain for the UDP rendez vous
+ */
public static final String DEFAULT_DOMAIN = "127.255.255.255:"+DEFAULT_PORT;
- public String appName;
- private boolean ivyRunning = false;
- private String ready_message = null;
+
+ private static boolean debug = (System.getProperty("IVY_DEBUG")!=null) ;
+ private static int serial=0; /* an unique ID for each regexps */
private ServerSocket app;
private IvyWatcher watch;
private Thread server;
-
- private Hashtable regexp_out = new Hashtable();
private Hashtable callbacks = new Hashtable();
private Vector clients = new Vector();
private Vector ivyApplicationListenerList = new Vector();
private String messages_classes[] = null;
- private int myport; /* Application port number */
-
- public synchronized boolean ivyRunning(){ return ivyRunning; }
+ int applicationPort; /* Application port number */
+ boolean ivyRunning = false;
+ Hashtable regexp_out = new Hashtable();
+ String ready_message = null;
+
+ /**
+ * Readies the structures for the software bus connexion.
+ *
+ * All the dirty work is done un the start() method
+ * @see #start
+ * @param name The name of your Ivy agent on the software bus
+ * @param message The hellow message you will send once ready
+ * @param appcb A callback handling the notification of connexions and
+ * disconnections, may be null
+ */
public Ivy( String name, String message, IvyApplicationListener appcb) {
appName = name;
ready_message = message;
if ( appcb != null ) ivyApplicationListenerList.addElement( appcb );
}
- void classes( String msg_classes[] ) { messages_classes = msg_classes; }
-
- public Hashtable getRegexpOut() { return regexp_out; }
-
- public String getReadyMessage() { return ready_message; }
-
- void addClient( Socket socket ) throws IOException {
- IvyClient client = new IvyClient( this, socket);
- clients.addElement( client );
- }
-
- void removeClient( IvyClient client ) {
- clients.removeElement( client );
+ /**
+ * connects the Ivy bus to a domain or list of domains
+ * @param domainbus a domain of the form 10.0.0:1234, it is similar to the
+ * netmask without the trailing .255. This will determine the meeting point
+ * of the different applications. Right now, this is done with an UDP
+ * broadcast. Beware of routing problems ! You can also use a comma
+ * separated list of domains.
+ *
+ */
+ public void start(String domainbus) throws IvyException {
+ try {
+ app = new ServerSocket(0);
+ applicationPort = app.getLocalPort();
+ } catch (IOException e) {
+ throw new IvyException("can't open TCP service socket " + e );
+ }
+ traceDebug("TCP service open on port "+applicationPort);
+ watch = new IvyWatcher(this);
+ ivyRunning = true;
+ server = new Thread(this);
+ server.start();
+ watch.start(getDomain(domainbus));
}
- void callCallback(IvyClient client, Integer key, String msgarg) {
- IvyMessageListener callback=(IvyMessageListener)callbacks.get(key);
- if (callback==null){
- traceDebug("(callCallback) Not regexp matching id "+key.intValue());
- return;
+ /**
+ * disconnects from the Ivy bus.
+ */
+ public void stop() {
+ try {
+ ivyRunning = false;
+ watch.stop();
+ app.close();
+ for ( int i = 0 ; i < clients.size(); i++ ) {
+ IvyClient client = (IvyClient)clients.elementAt(i);
+ client.close("normal Ivy Stopping...");
+ // advertise that this is a normal
+ //close for debugging purposes
+ }
+ } catch (IOException e) {
+ traceDebug("IOexception Stop ");
}
- StringTokenizer st = new StringTokenizer(msgarg,IvyClient.EndArg);
- String args[] = new String[st.countTokens()];
- int i=0;
- while (st.hasMoreTokens()){ args[i++] = st.nextToken();}
- callback.receive( client, args );
+ clients.removeAllElements();
}
- public int getApplicationPort() { return myport; }
-
+ /**
+ * Performs a pattern matching according to everyone's regexps, and sends
+ * the results to the relevant ivy agents.
+ * <p><em>There is one thread for each client connected, we could also
+ * create another thread each time we send a message.</em>
+ * @param message A String which will be compared to the regular
+ * expressions of the different clients
+ * @return the number of messages actually sent
+ */
public int sendMsg( String message ) {
- int count = 0;
- // envoie le message à tous les clients du bus
- // TODO: il faudrait mettre une Thread emission par client */
+ int count = 0;
+ // TODO: il faudrait mettre une Thread emission par client */
for ( int i = 0 ; i < clients.size(); i++ ) {
IvyClient client = (IvyClient)clients.elementAt(i);
count += client.sendMsg( message );
}
- return count;
+ return count;
}
- static private int serial=0;
+ /**
+ * Subscribes to a regular expression.
+ *
+ * The callback will be executed with
+ * the saved parameters of the regexp as arguments when a message will sent
+ * by another agent. A program <em>doesn't</em> receive its own messages.
+ * <p>Example:
+ * <br>the Ivy agent A performs <pre>b.bindMsg("^Hello (*)",cb);</pre>
+ * <br>the Ivy agent B performs <pre>b2.sendMsg("Hello world");</pre>
+ * <br>a thread in A will uun the callback cb with its second argument set
+ * to a array of String, with one single element, "world"
+ * @param regexp a perl regular expression, groups are done with parenthesis
+ * @param callback any objects implementing the IvyMessageListener
+ * interface, on the AWT/Swing framework
+ * @return the id of the regular expression
+ */
public int bindMsg(String regexp, IvyMessageListener callback ) {
- // associe une nouvelle regexp à un nouveau callback
+ // creates a new binding (regexp,callback)
Integer key = new Integer(serial++);
regexp_out.put(key,regexp);
callbacks.put(key,callback );
- // indique aux autres clients la nouvelle regexp
+ // notifies the other clients this new regexp
for (int i=0;i<clients.size();i++){
((IvyClient)clients.elementAt(i)).sendRegexp(key.intValue(),regexp);
}
return key.intValue();
}
- public void unBindMsg(int id) {
+ /**
+ * unsubscribes a regular expression
+ *
+ * @param id the id of the regular expression, returned when it was bound
+ */
+ public void unBindMsg(int id) throws IvyException {
Integer key = new Integer(id);
if ( ( regexp_out.remove(key) == null )
|| (callbacks.remove(key) == null ) ) {
- System.err.println("attention j'enlève une regexp inexistante");
+ throw new IvyException("client wants to remove an unexistant regexp "+id);
}
for (int i=0;i<clients.size();i++ ) {
- ((IvyClient)clients.elementAt(i)).delRegexp(id );
+ ((IvyClient)clients.elementAt(i)).delRegexp(id );
}
}
- /* gestion des applications listener et callback associe */
+
+ /**
+ * adds an application listener to a bus
+ * @param callback is an object implementing the IvyApplicationListener
+ * interface
+ * @return the id of the application listener, useful if you wish to remove
+ * it later
+ */
public int addApplicationListener(IvyApplicationListener callback){
- // associe une nouvelle regexp à un nouveau callback
- ivyApplicationListenerList.addElement( callback );
+ ivyApplicationListenerList.addElement(callback);
int id = ivyApplicationListenerList.indexOf( callback );
return id;
}
- public void removeApplicationListener( int id ) {
- ivyApplicationListenerList.removeElementAt(id);
+
+ /**
+ * removes an application listener
+ * @param id the id of the application listener to remove
+ */
+ public void removeApplicationListener(int id){
+ ivyApplicationListenerList.removeElementAt(id);
}
+
+ /* invokes the application listeners upon arrival of a new Ivy client
+ * it *might* be considered poor style to invoke them as the same level
+ * as the others applicationListeners. This is part of the interface
+ */
public void connect(IvyClient client){
- for ( int i = 0 ; i < ivyApplicationListenerList.size(); i++ )
- {
- IvyApplicationListener listener = (IvyApplicationListener)ivyApplicationListenerList.elementAt(i);
- listener.connect(client);
- }
+ for ( int i = 0 ; i < ivyApplicationListenerList.size(); i++ ) {
+ ((IvyApplicationListener)ivyApplicationListenerList.elementAt(i)).connect(client);
+ }
}
+
+ /* invokes the application listeners upon departure of an Ivy client
+ * ibid.
+ */
public void disconnect(IvyClient client){
- for ( int i = 0 ; i < ivyApplicationListenerList.size(); i++ )
- {
- IvyApplicationListener listener = (IvyApplicationListener)ivyApplicationListenerList.elementAt(i);
- listener.disconnect(client);
- }
+ for ( int i = 0 ; i < ivyApplicationListenerList.size(); i++ ) {
+ ((IvyApplicationListener)ivyApplicationListenerList.elementAt(i)).disconnect(client);
+ }
}
+
+ /* invokes the application listeners upon death of an Ivy client
+ * ibid
+ */
public void die(IvyClient client, int id){
- for ( int i = 0 ; i < ivyApplicationListenerList.size(); i++ )
- {
- IvyApplicationListener listener = (IvyApplicationListener)ivyApplicationListenerList.elementAt(i);
- listener.die(client, id);
- }
+ for ( int i = 0 ; i < ivyApplicationListenerList.size(); i++ ) {
+ ((IvyApplicationListener)ivyApplicationListenerList.elementAt(i)).die(client, id);
+ }
}
+
+ /* invokes the direct message callbacks
+ * ibid
+ */
public void directMessage( IvyClient client, int id,String msgarg ){
- for ( int i = 0 ; i < ivyApplicationListenerList.size(); i++ )
- {
- IvyApplicationListener listener = (IvyApplicationListener)ivyApplicationListenerList.elementAt(i);
- listener.directMessage(client,id, msgarg);
- }
+ for ( int i = 0 ; i < ivyApplicationListenerList.size(); i++ ) {
+ ((IvyApplicationListener)ivyApplicationListenerList.elementAt(i)).directMessage(client,id, msgarg);
+ }
}
- public static String getDomain(String domainbus) throws IvyException {
- if ( domainbus == null )
- domainbus = System.getProperty("IVYBUS");
- if ( domainbus == null )
- domainbus = DEFAULT_DOMAIN;
- return domainbus;
+ /////////////////////////////////////////////////////////////////:
+ //
+ // Protected methods
+ //
+ /////////////////////////////////////////////////////////////////:
+
+ void addClient(Socket socket,boolean peerCalling) throws IOException {
+ IvyClient client = new IvyClient(this, socket,peerCalling);
+ clients.addElement(client);
}
- public void start(String domainbus) throws IvyException {
- try {
- app = new ServerSocket(0);
- myport = app.getLocalPort();
- } catch (IOException e) {
- throw new IvyException("ne peut pas ouvrir la socket server" + e );
- }
- /* Start watcher thread */
- watch = new IvyWatcher(this);
- traceDebug("BUS TCP: "+myport);
- ivyRunning = true;
- server = new Thread( this);
- /* Start ivy server thread */
- server.start();
- /* Send ivy helo msg */
- watch.sendStart(getDomain(domainbus));
+
+ void removeClient( IvyClient client ) {
+ clients.removeElement( client );
}
- public void stop() {
- try {
- ivyRunning = false;
- watch.close(); /* Stop watcher thread */
- app.close(); /* Stop ivy server thread */
- for ( int i = 0 ; i < clients.size(); i++ ) {
- IvyClient client = (IvyClient)clients.elementAt(i);
- client.close("Ivy Stopping...");/* Stop client thread */
- }
- } catch ( IOException e ) {
- traceDebug("ioexception Stop ");
+ void callCallback(IvyClient client, Integer key, String msgarg) throws IvyException {
+ IvyMessageListener callback=(IvyMessageListener)callbacks.get(key);
+ if (callback==null){
+ throw new IvyException("(callCallback) Not regexp matching id "+key.intValue());
}
- clients.removeAllElements();
+ StringTokenizer st = new StringTokenizer(msgarg,IvyClient.EndArg);
+ String args[] = new String[st.countTokens()];
+ int i=0;
+ while (st.hasMoreTokens()){ args[i++] = st.nextToken();}
+ callback.receive( client, args );
}
-
+
+
+ static String getDomain(String domainbus) throws IvyException {
+ if ( domainbus == null ) domainbus = System.getProperty("IVYBUS");
+ if ( domainbus == null ) domainbus = DEFAULT_DOMAIN;
+ return domainbus;
+ }
+
+
+ /*
+ * checks the "validity" of a regular expression.
+ * TODO i'm not sure this is still used by anything.
+ */
boolean CheckRegexp( String exp ) {
- /* accepte tout par default */
boolean regexp_ok = true;
if ( exp.startsWith( "^" )&&messages_classes!=null) {
regexp_ok=false;
@@ -194,7 +288,12 @@ public class Ivy implements Runnable, IvyApplicationListener {
return regexp_ok;
}
- public boolean checkConnected( IvyClient clnt ) {
+ /*
+ * TODO prevents two clients from connecting to each other at the same time
+ * there is still a lingering bug here, that we could avoid with the
+ * SchizoToken.
+ */
+ boolean checkConnected( IvyClient clnt ) {
if ( clnt.getAppPort() == 0 ) return false;
for ( int i = 0 ; i < clients.size(); i++ ) {
IvyClient client = (IvyClient)clients.elementAt(i);
@@ -203,25 +302,36 @@ public class Ivy implements Runnable, IvyApplicationListener {
return false;
}
+ /*
+ * the service socket thread reader.
+ */
public void run() {
- Socket socket;
- while( ivyRunning() ) {
- try {
- socket = app.accept();
- addClient( socket );
- } catch( IOException e ) {
- /* Normal terminaison si socket close */
- /* TODO: et les autres */
- traceDebug("Error IvyServer exception: " + e.getMessage());
- }
- }
+ while(ivyRunning){
+ try {
+ Socket socket = app.accept();
+ addClient(socket,true); // the peer called me
+ } catch( IOException e ) {
+ /* TODO is it a normal termination on socket close ? */
+ traceDebug("Error IvyServer exception: " + e.getMessage());
+ System.out.println("DEBUG TCP socket reader caught an exception " + e.getMessage());
+ }
+ }
+ traceDebug("IvyServer end of trans");
}
+
+ /////////////////////////////////////////////////////////////////:
+ //
+ // Private methods
+ //
+ /////////////////////////////////////////////////////////////////:
private void traceDebug(String s){
if (debug) System.out.println("-->ivy<-- "+s);
}
-
-
+ // TODO find out if this is useful or not ...
+ private void classes( String msg_classes[] ) {
+ messages_classes = msg_classes;
+ }
-} // class Ivy
+}
diff --git a/src/IvyApplicationAdapter.java b/src/IvyApplicationAdapter.java
index 16491b5..af198dd 100755
--- a/src/IvyApplicationAdapter.java
+++ b/src/IvyApplicationAdapter.java
@@ -1,14 +1,20 @@
-/*
-**
-** BusCallbackAdapter
-**
-** la même pratique que pour l awt 1.1. Quand on ne veut implémenter qu une
-** des trois fonctions membres, il suffit d étendre l'adapter
-** pas de constructeur ... c'est donc une class abstraite
-*/
-
package fr.dgac.ivy;
+
+/**
+ * this class is a dummy ApplicationListener
+ *
+ * @author François-Régis Colin
+ * @author Yannick Jestin
+ * @author <a href="http://www.tls.cena.fr/products/ivy/">http://www.tls.cena.fr/products/ivy/</a>
+ *
+ * an ApplicatinListener class for handling application-level request on the
+ * Ivy bus. The methods in this class are empty. This class exists as a
+ * convenience for implementing a subset of the methods of the
+ * applicationlistener. See the AWT 1.1 framework for further information on
+ * this.
+ */
+
public abstract class IvyApplicationAdapter implements IvyApplicationListener {
public void connect( IvyClient client ) { }
public void disconnect( IvyClient client ) { }
diff --git a/src/IvyApplicationListener.java b/src/IvyApplicationListener.java
index 731f8a7..bdfb12c 100755
--- a/src/IvyApplicationListener.java
+++ b/src/IvyApplicationListener.java
@@ -1,9 +1,38 @@
package fr.dgac.ivy;
-public interface IvyApplicationListener extends java.util.EventListener
-{
- public abstract void connect(IvyClient client);
- public abstract void disconnect(IvyClient client);
- public abstract void die(IvyClient client, int id);
- public abstract void directMessage( IvyClient client, int id,String msgarg );
+/**
+ * this interface specifies the methods of an ApplicationListener
+ *
+ * @author François-Régis Colin
+ * @author Yannick Jestin
+ * @author <a href="http://www.tls.cena.fr/products/ivy/">http://www.tls.cena.fr/products/ivy/</a>
+ *
+ * The ApplicatinListenr for receiving application level events on the Ivy
+ * bus: connexion, disconnexion, direct messages or requests to quit.
+ */
+
+public interface IvyApplicationListener extends java.util.EventListener {
+ /**
+ * invoked when a Ivy Client has joined the bus
+ * @param client the peer
+ */
+ public abstract void connect(IvyClient client);
+ /**
+ * invoked when a Ivy Client has left the bus
+ * @param client the peer
+ */
+ public abstract void disconnect(IvyClient client);
+ /**
+ * invoked when a peer request us to leave the bus
+ * @param client the peer
+ */
+ public abstract void die(IvyClient client, int id);
+ /**
+ * invoked when a peer sends us a direct message
+ * @param client the peer
+ * @param id
+ * @param msgarg the message itself
+ * this is not yet implemented in java. I believe it has no real use :)
+ */
+ public abstract void directMessage( IvyClient client, int id,String msgarg );
}
diff --git a/src/IvyClient.java b/src/IvyClient.java
index 7ec220f..dd0f156 100755
--- a/src/IvyClient.java
+++ b/src/IvyClient.java
@@ -1,7 +1,3 @@
-/*
-** IvyClient
-*/
-
package fr.dgac.ivy ;
import java.lang.Thread;
@@ -10,121 +6,93 @@ import java.io.*;
import java.util.*;
import gnu.regexp.*;
-public class IvyClient extends Thread {
+/**
+ * A private Class for the the peers on the bus
+ *
+ * @author François-Régis Colin
+ * @author Yannick Jestin
+ * @author <a href="http://www.tls.cena.fr/products/ivy/">http://www.tls.cena.fr/products/ivy/</a>
+ *
+ * each time a connexion is made with a remote peer, the regexp are exchanged
+ * once ready, a ready message is sent, and then we can send messages,
+ * die messages, direct messages, add or remove regexps, or quit. A thread is
+ * created for each remote client.
+ */
+
+class IvyClient extends Thread {
- /*
- * les types de messages
- * on ne *change* pas, sous peine d'incompatibilité avec les autres
- * implémentations ( C, C++, etc... ).
- * quoi que :)
- *
- */
- public final static int Bye = 0; /* l'application emettrice se termine */
- public final static int AddRegexp = 1;/* expression reguliere d'un client */
- public final static int Msg = 2 ; /* message reel */
- public final static int Error = 3; /* error message */
- public final static int DelRegexp = 4;/* Remove expression reguliere */
- public final static int EndRegexp = 5;/* fin de liste regexp */
- public final static int StartRegexp = 6;/* debut des expressions */
- public final static int DirectMsg = 7;/* message direct a l'appli */
- public final static int Die = 8; /* demande de terminaison de l'appli */
- public final static int Ping = 9; /* demande de réponse pong */
- public final static int Pong = 10; /* réponse au ping */
- public final static String StartArg = "\002";/* separateur debut args */
- public final static String EndArg = "\003"; /* separateur inter arguments */
-
- private String appName;
- private int appPort;
+ /* the protocol magic numbers */
+ final static int Bye = 0; /* end of the peer */
+ final static int AddRegexp = 1;/* the peer adds a regexp */
+ final static int Msg = 2 ; /* the peer sends a message */
+ final static int Error = 3; /* error message */
+ final static int DelRegexp = 4;/* the peer removes one of his regex */
+ final static int EndRegexp = 5;/* no more regexp in the handshake */
+ final static int SchizoToken = 6; /* avoid race condition in concurrent connexions */
+ final static int DirectMsg = 7;/* the peer sends a direct message */
+ final static int Die = 8; /* the peer wants us to quit */
+ final static String StartArg = "\002";/* begin of arguments */
+ final static String EndArg = "\003"; /* end of arguments */
- /*
- * gestion du protocole ping
- * ne marche pas encore
- */
- private long lastRead = 0;
- private long pingDate = 0 ;
- private long pingLag =0 ;
-
+ private static boolean debug = (System.getProperty("IVY_DEBUG")!=null) ;
private Ivy bus;
private Socket socket;
private BufferedReader in;
private OutputStream out;
private Hashtable regexp_in = new Hashtable();
+ private Hashtable regexp_text = new Hashtable();
+ private String appName;
+ private int appPort;
+ private boolean gardefou=true;
+ private boolean peerCalling;
- static private boolean debug = (System.getProperty("IVY_DEBUG")!=null) ;
-
- IvyClient( Ivy bus, Socket socket ) throws IOException {
+ IvyClient(Ivy bus, Socket socket,boolean peerCalling) throws IOException {
appName = "Unkown";
appPort = 0;
this.bus = bus;
this.socket = socket;
- lastRead = (new Date()).getTime();
+ this.peerCalling=peerCalling;
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = socket.getOutputStream();
- sendService( bus.getRegexpOut());
- start();
- }
-
- /*
- * quelques accesseurs
- */
- public InetAddress getRemoteAddress() { return socket.getInetAddress(); }
- public String getApplicationName() { return appName ; }
- public int getAppPort() { return appPort ; }
-
- void sendBuffer( String buffer ) {
- buffer += "\n";
- try {
- out.write(buffer.getBytes() );
- out.flush();
- } catch ( IOException e ) {
- // TODO: write error
- System.err.println("IvyClient.sendBuffer.write failed. FIX ME");
+ Hashtable regexps=bus.regexp_out;
+ // sends our ID, whether we initiated the connexion or not
+ // the ID is the couple "host name,application Port", the host name
+ // information is in the socket itself, the port is not known if we
+ // initiate the connexion
+ send(SchizoToken,bus.applicationPort,bus.appName);
+ // sends our regexps to the peer
+ for (Enumeration e = regexps.keys(); e.hasMoreElements(); ) {
+ Integer key = (Integer)e.nextElement();
+ sendRegexp( key.intValue(),(String)regexps.get(key));
}
+ send( EndRegexp,0,"");
+ // spawns a thread to manage the incoming traffic on this
+ // socket. We should be ready to receive messages now.
+ start();
}
- void send( int type, int id, String arg) {
- String buffer = type+" "+id+StartArg+arg;
- sendBuffer(buffer );
- }
-
- void sendRegexp( int id, String regexp ) { send( AddRegexp,id,regexp); }
- public void sendDie( ) { send( Die,0,""); }
+ String getApplicationName() { return appName ; }
- public void sendDie(String message) { send( Die,0,message); }
-
- void sendPing() {
- pingDate = (new Date()).getTime();
- // on marque le message par la date actuelle
- send( Ping, 0, String.valueOf(pingDate) );
- }
-
- /*
- * notifie la liste des regexps aux autres clients du bus
+ /**
+ * allow an Ivy package class to access the list of regexps at a
+ * given time.
+ * perhaps we should implement a new IvyApplicationListener method to
+ * allow the notification of regexp addition and deletion
*/
- private void sendService( Hashtable regexps ) {
- send( StartRegexp, bus.getApplicationPort(), bus.appName);
- for (Enumeration e = regexps.keys(); e.hasMoreElements(); ) {
- Integer key = (Integer)e.nextElement();
- sendRegexp( key.intValue(),(String)regexps.get(key));
- }
- send( EndRegexp,0, "");
- }
+ Enumeration getRegexps() { return regexp_text.elements(); }
+ int getAppPort() { return appPort ; }
- void send( int type, Integer id, int nbsub, REMatch result) {
- String buffer = type+" "+id+StartArg;
- traceDebug ( "Send matching result \n" );
- // ????Start at 1 because group 0 represent entire matching
- for(int sub = 1; sub <= nbsub; sub++) {
- if (result.getSubStartIndex(sub) > -1) {
- buffer += result.toString(sub)+EndArg;
- traceDebug( "Send arg "+result.toString(sub));
- }
- }
- sendBuffer( buffer );
- }
+ /* perhaps we should perform some checking here */
+ void sendRegexp(int id,String regexp) {send(AddRegexp,id,regexp);}
+ public void delRegexp(int id) {send( DelRegexp,id,"");}
- public int sendMsg( String message ) {
+ /**
+ * sends the substrings of a message to the peer for each matching regexp.
+ * @param message the string that will be match-tested
+ * @return the number of messages sent to the peer
+ */
+ int sendMsg( String message ) {
int count = 0;
for (Enumeration e = regexp_in.keys();e.hasMoreElements();) {
Integer key = (Integer)e.nextElement();
@@ -138,152 +106,178 @@ public class IvyClient extends Thread {
return count;
}
+ /**
+ * closes the connexion to the peer.
+ * @param msg the debug information
+ * the thread managing the socket is stopped
+ */
void close(String msg) throws IOException {
traceDebug( msg );
- socket.close();
- //stop();
-}
+ socket.close(); // TODO it seems it doesnt stop the thread
+ out.close();
+ in.close();
+ gardefou=false;
+ }
- public boolean sameClient( IvyClient clnt ) {
+ /**
+ * compares two peers the id is the couple (host,service port).
+ * @param clnt the other peer
+ * @return true if the peers are similir. This should not happen, it is bad
+ * © ® (tm)
+ */
+ boolean sameClient( IvyClient clnt ) {
return ( appPort != 0 && appPort == clnt.appPort )
&& ( getRemoteAddress() == clnt.getRemoteAddress() ) ;
}
- public void delRegexp( int id ) { send( DelRegexp,id,"");}
-
+ /**
+ * the code of the thread handling the incoming messages.
+ * this thread stops (at least it should) when the socket is closed
+ * or when gardefou=false
+ */
public void run() {
String msg = null;
try {
- traceDebug("Connected from "+
- socket.getInetAddress().getHostName()+":"+socket.getPort());
- while((msg=in.readLine()) != null ) {
- int msgtype; /* type du dernier message recu */
- Integer msgid; /* id du dernier message recu */
- RE regexp = null; /* regexp compile */
+ traceDebug("Connected from "+ socket.getInetAddress().getHostName()+
+ ":"+socket.getPort());
+ while ( gardefou && ((msg=in.readLine()) != null )) {
+ int msgtype; // type of the last message received
+ Integer msgid; // ID of the last message received
+ RE regexp = null; // compiled regexp
String token = null;
- /*
- * ETAPE UN : extraire le message
+ /*
+ * First stage: extract the message
*/
- lastRead = (new Date()).getTime();
StringTokenizer st = new StringTokenizer(msg);
- if (!st.hasMoreTokens()){
- close("Bad format no type '"+msg+"'");
- break;
- }
+ if(!st.hasMoreTokens()){close("Bad format no type '"+msg+"'");break;}
token=st.nextToken();
- if (token.length()==0) {
- close("Bad format no type '"+msg+"'");
- break;
- }
+ if (token.length()==0){close("Bad format no type '"+msg+"'");break;}
try {
msgtype = Integer.parseInt(token);
} catch ( NumberFormatException e ) {
close("Bad format error parsing type'"+msg+"'");
break;
}
- if (!st.hasMoreTokens()) {
- close("Bad format no id '"+msg+"'");
- break;
- }
- msgid=Integer.valueOf(st.nextToken(StartArg));
+ if(!st.hasMoreTokens()){close("Bad format no id '"+msg+"'");break;}
+ token=st.nextToken(StartArg);
/*
- * DONE: il trainait un bug ici: on oubliait de réinitialiser msgarg
- * à la chaine vide, et du coup, pour les regexps sans groupe (),
- * ça envoyait des messages un peu fantaisistes
+ * TODO
+ * this doesn't work on jdk1.3 !!!
+ * the token equals " 3992", which provoques a NumberFormatException
*/
+ try {
+ msgid=Integer.valueOf(token);
+ } catch ( NumberFormatException e ) {
+ close("Bad format error parsing id '"+token+"'");
+ break;
+ }
String msgarg="";
- if (st.hasMoreTokens()) msgarg=st.nextToken("\n");
-
- /*
- *ETAPE DEUX : traitement adapté au type de message
+ if (st.hasMoreTokens()) msgarg=st.nextToken("\n");
+ /*
+ * second stage: process the message
*/
- switch (msgtype) {
- case Bye: break;
- case AddRegexp:
+ switch (msgtype) {
+ case Bye: break;
+ case AddRegexp:
if ( bus.CheckRegexp(msgarg) ) {
try {
regexp_in.put(msgid,new RE(msgarg));
- } catch (REException e) {
+ regexp_text.put(msgid,msgarg);
+ } catch (REException e) {
System.err.println("Bad pattern: "+e.getMessage());
}
- } else {
+ } else {
System.err.println(
- "Warning exp='"+msgarg+"' can't match removing from "+appName);
+ "Warning exp='"+msgarg+"' can't match removing from "+appName);
}
- break;
- case DelRegexp:
+ break;
+ case DelRegexp:
regexp_in.remove(msgid);
- break;
- case EndRegexp:
- /*
- * call application callback avec event Connected
- */
- bus.connect(this);
- if (bus.getReadyMessage()!=null) sendMsg(bus.getReadyMessage());
- break;
- case Msg:
- /*
- * call callback avec les arguments
- */
- bus.callCallback(this,msgid,msgarg);
- break;
- case Error:
- traceDebug("Error msg "+msgid+" "+msgarg);
- break;
- case StartRegexp:
- appName=msgarg;
- appPort=msgid.intValue();
- if ( bus.checkConnected(this) )
- close("Quitting Application already connected");
- break;
- case DirectMsg:
- traceDebug("Direct Message id="+msgid+" msg='"+msgarg+"'");
- /*
- * call directCallback avec les arguments
- */
- bus.directMessage( this, msgid.intValue(), msgarg );
- break;
- case Die:
- traceDebug("Die Message received . argh !");
- /*
- * call diecallBack aavant de quitter
+ regexp_text.remove(msgid);
+ break;
+ case EndRegexp:
+ bus.connect(this);
+ /* TODO
+ * BUG ? the peer is perhaps not ready to handle this message
+ * an assymetric processing should be written
*/
- bus.die( this,msgid.intValue());
- break;
- case Ping:
- traceDebug("Ping Message");
- // répond avec le même argument .. Pour le moment c'est inutile
- send(Pong,0,msgarg);
+ if (bus.ready_message!=null) sendMsg(bus.ready_message);
break;
- case Pong:
- // calcul du lag en millisecondes
- traceDebug("Pong Message");
- pingLag = (new Date()).getTime() - pingDate ;
+ case Msg:
+ try {
+ bus.callCallback(this,msgid,msgarg);
+ } catch (IvyException ie) {
+ // calling an inexistant callback
+ System.err.println("calling an inexistant callback, the caller must be wrong !");
+ }
break;
- default:
- System.err.println("*** IvyClient *** unhandled msg type "+
- msgtype+" "+msgid+msgarg);
- break;
- } // switch sur le type de message
- } // while readline
-
- // plus rien à lire .... ou alors break donc erreur
- traceDebug("zarbi Disconnected from "+
+ case Error:
+ traceDebug("Error msg "+msgid+" "+msgarg);
+ break;
+ case SchizoToken:
+ appName=msgarg;
+ appPort=msgid.intValue();
+ if ( bus.checkConnected(this) ) {
+ close("Quitting Application already connected");
+ System.err.println("Rare ! A concurrent connect occured");
+ }
+ break;
+ case DirectMsg:
+ bus.directMessage( this, msgid.intValue(), msgarg );
+ break;
+ case Die:
+ bus.die( this,msgid.intValue());
+ break;
+ default:
+ System.err.println("*** IvyClient *** unhandled msg type "+
+ msgtype+" "+msgid+msgarg);
+ break;
+ } // switch
+ } // while gardefou
+ traceDebug("normally Disconnected from "+
+ socket.getInetAddress().getHostName()+":"+socket.getPort());
+ } catch (IOException e) {
+ traceDebug("abnormally Disconnected from "+
socket.getInetAddress().getHostName()+":"+socket.getPort());
- } catch ( IOException e ) {
- traceDebug("ioexception Disconnected from "+
- socket.getInetAddress().getHostName()+":"+socket.getPort());
}
- /* je meurs en tant que client du bus */
- /* mais avant, j'exécute mon application callback perso */
bus.disconnect( this );
bus.removeClient( this );
- } // run
+ }
- public String From() { return socket.toString(); }
+ private void sendBuffer( String buffer ) {
+ buffer += "\n";
+ try {
+ out.write(buffer.getBytes() );
+ out.flush();
+ } catch ( IOException e ) {
+ /*
+ * TODO
+ * we should throw an exception here
+ */
+ System.err.println("IvyClient.sendBuffer.write failed. FIX ME");
+ }
+ }
+
+ private void send(int type, int id, String arg) {
+ sendBuffer(type+" "+id+StartArg+arg);
+ }
+
+ private void send(int type, Integer id, int nbsub, REMatch result) {
+ String buffer = type+" "+id+StartArg;
+ // Start at 1 because group 0 represent entire matching
+ for(int sub = 1; sub <= nbsub; sub++) {
+ if (result.getSubStartIndex(sub) > -1) {
+ buffer += result.toString(sub)+EndArg;
+ }
+ }
+ sendBuffer(buffer);
+ }
+ private void sendDie() {send(Die,0,"");}
+ private void sendDie(String message) {send(Die,0,message);}
+ private InetAddress getRemoteAddress() { return socket.getInetAddress(); }
private void traceDebug(String s){
- if (debug) System.out.println("-->ivyclient<-- "+s);
+ if (debug) System.out.println("-->IvyClient<-- "+s);
}
} // class IvyClient
/* EOF */
diff --git a/src/IvyException.java b/src/IvyException.java
index 263631b..e467771 100755
--- a/src/IvyException.java
+++ b/src/IvyException.java
@@ -1,5 +1,14 @@
package fr.dgac.ivy;
+/**
+ * signals that an unrecoverrable Ivy exception has occured.
+ *
+ * @author François-Régis Colin
+ * @author Yannick Jestin
+ * @author <a href="http://www.tls.cena.fr/products/ivy/">http://www.tls.cena.fr/products/ivy/</a>
+ *
+ */
+
public class IvyException extends Exception {
- public IvyException(String s) { super(s); }
+ IvyException(String s) { super(s); }
}
diff --git a/src/IvyMessageListener.java b/src/IvyMessageListener.java
index c0bfde4..e960afb 100755
--- a/src/IvyMessageListener.java
+++ b/src/IvyMessageListener.java
@@ -1,6 +1,20 @@
package fr.dgac.ivy;
+/**
+ * this interface specifies the methods of an IvyMessageListener
+ *
+ * @author François-Régis Colin
+ * @author Yannick Jestin
+ * @author <a href="http://www.tls.cena.fr/products/ivy/">http://www.tls.cena.fr/products/ivy/</a>
+ *
+ */
+
public interface IvyMessageListener extends java.util.EventListener
{
+ /**
+ * this callback is invoked when a message has been received
+ * @param client the peer who sent the message
+ * @param args the array of string, on string for each subregexp
+ */
public abstract void receive(IvyClient client, String[] args);
}
diff --git a/src/IvyWatcher.java b/src/IvyWatcher.java
index 21311e9..bae387a 100755
--- a/src/IvyWatcher.java
+++ b/src/IvyWatcher.java
@@ -1,8 +1,3 @@
-/*
-*
-* IvyWatcher : gestion des messages UDP pour annoncer les arrivées
-*
-*/
package fr.dgac.ivy ;
import java.lang.Thread;
@@ -11,156 +6,171 @@ import java.io.*;
import java.util.StringTokenizer;
import gnu.regexp.*;
import java.util.Vector;
-public class IvyWatcher extends Thread {
- Ivy bus; /* master bus controler */
- DatagramSocket broadcast; /* supervision socket */
- private Vector domainaddrList;
+
+/**
+ * A private Class for the Ivy rendezvous
+ *
+ * @author François-Régis Colin
+ * @author Yannick Jestin
+ * @author <a href="http://www.tls.cena.fr/products/ivy/">http://www.tls.cena.fr/products/ivy/</a>
+ *
+ * right now, the rendez vous is on a UDP socket. The watcher will answer to
+ * each peer advertising its arrival on the bus. The intrinsics of Unix are so
+ * that the broadcast is done using the same socket, which is not a good
+ * thing.
+ */
+
+class IvyWatcher implements Runnable {
private static boolean debug = (System.getProperty("IVY_DEBUG")!=null);
+ private Vector domainaddrList;
+ private boolean watcherrunning = false;
+ private Thread broadcastListener ;
+ private Ivy bus; /* master bus controler */
+ private DatagramSocket broadcast; /* supervision socket */
+
+ /**
+ * creates an Ivy watcher.
+ * @param bus the bus
+ */
+ IvyWatcher(Ivy bus) throws IvyException {
+ this.bus = bus;
+ domainaddrList = new Vector();
+ }
- public void close() {
+ /**
+ * the behaviour of the thread watching the UDP socket.
+ * this thread will stop either when the bus stops or when the
+ * watcherrunning will be set to false
+ */
+ public void run() {
+ traceDebug("IvyWatcher waiting for Broadcast");
+ while( watcherrunning && bus.ivyRunning ) try {
+ byte buf[] = new byte[256];
+ DatagramPacket packet=new DatagramPacket(buf, 256);
+ int port;
+ broadcast.receive(packet);
+ String msg = new String(packet.getData()) ;
+ InetAddress remotehost = packet.getAddress();
+ traceDebug("BUSWATCHER Receive Broadcast from "+
+ remotehost.getHostName()+":"+packet.getPort());
+ // check if remoteaddr is in our broadcast domain list
+ // otherwise we ignore the broadcast
+ if ( !isInDomain( remotehost ) ) continue;
+ StringTokenizer st = new StringTokenizer(msg);
+ if ( !st.hasMoreTokens()) {
+ System.err.println("Bad format "+msg);
+ continue;
+ }
+ int version = Integer.parseInt( st.nextToken() );
+ if ( version != bus.PROCOCOLVERSION ) {
+ System.err.println("Ignoring bad protocol version broadcast");
+ continue;
+ }
+ if ( ! st.hasMoreTokens()) {
+ System.err.println("Bad format "+msg);
+ continue;
+ }
+ port = Integer.parseInt( st.nextToken() );
+ if ( (bus.applicationPort == port) ) continue;
+ traceDebug("BUSWATCHER Broadcast de "
+ +packet.getAddress().getHostName()
+ +":"+packet.getPort()+" port "+port+" version "+version);
+ try {
+ Socket socket = new Socket( remotehost, port );
+ bus.addClient(socket,false);
+ } catch ( UnknownHostException e ) {
+ System.err.println("Unkonwn host "+remotehost + e.getMessage());
+ } catch ( IOException e) {
+ System.err.println("can't connect to "+remotehost+" port "+
+ port+e.getMessage());
+ }
+ } catch ( IOException e ) {
+ watcherrunning=false;
+ traceDebug("broadcast listener crashed " + e.getMessage());
+ }
broadcast.close();
+ traceDebug("broadcast listener normal shutdown");
}
- private void sendBroadcast( String data, String net ) throws IvyException
- {
- DatagramPacket packet;
- // transformation en forme de broadcast w.x.y.z en w.x.y.255
- try {
- // simple trick to expend to 255 thank's Alex
- net += ".255.255.255";
- RE exp = new RE( "^(\\d+\\.\\d+\\.\\d+\\.\\d+).*");
- net = exp.substitute( net , "$1" );
- } catch ( REException e ){
- throw new IvyException("Bad broascat addr");
- }
- // broadcast sur l'adresse iadr
+
+ /**
+ * stops the thread waiting on the broadcast socket
+ */
+ void stop() { watcherrunning=false; }
+
+ private void sendBroadcast(String data, String net) throws IvyException {
try {
- InetAddress iaddr = InetAddress.getByName(net);
- domainaddrList.addElement( iaddr );
- packet = new DatagramPacket(
+ // simple trick to expand to 255 (Alex Bustico)
+ net += ".255.255.255";
+ RE exp = new RE( "^(\\d+\\.\\d+\\.\\d+\\.\\d+).*");
+ net = exp.substitute( net , "$1" );
+ } catch ( REException e ){
+ throw new IvyException("Bad broascat addr");
+ }
+ try {
+ InetAddress iaddr = InetAddress.getByName(net);
+ domainaddrList.addElement(iaddr);
+ DatagramPacket packet = new DatagramPacket(
data.getBytes(),
data.length(),
iaddr,
broadcast.getLocalPort() );
- broadcast.send( packet );
+ broadcast.send(packet);
} catch ( UnknownHostException e ) {
- throw new IvyException("Broadcast sur réseau inconnu " + e );
+ throw new IvyException("Broadcast sent on unknown network "+
+ e.getMessage());
} catch ( IOException e ) {
- throw new IvyException("broadcast send erreur " + e );
+ throw new IvyException("Broadcast error " + e.getMessage() );
}
}
- public void sendStart(String domain) throws IvyException {
- String domainaddr;
- // parse Domain to get port number
- int port;
- int sep_index = domain.lastIndexOf( ":" );
- if ( sep_index == -1 )
- {
- port = bus.DEFAULT_PORT;
- domainaddr = domain;
- }
- else
- {
- port = Integer.parseInt( domain.substring( sep_index +1 ));
- domainaddr = domain.substring(0,sep_index);
- }
- // create the UDP socket
- try {
+
+ void start(String domain) throws IvyException {
+ String domainaddr;
+ // parse Domain to get port number
+ int port;
+ int sep_index = domain.lastIndexOf( ":" );
+ if ( sep_index == -1 ) {
+ port = bus.DEFAULT_PORT;
+ domainaddr = domain;
+ } else {
+ port = Integer.parseInt( domain.substring( sep_index +1 ));
+ domainaddr = domain.substring(0,sep_index);
+ }
+ // create the UDP socket
+ try {
broadcast = new MulticastSocket(port );
} catch ( IOException e ) {
throw new IvyException("IvyWatcher erreur I/O" + e );
}
- // start UDP receiver
- start();
- // send hello world on UDP sockets
- String hello = "3 "+bus.getApplicationPort()+"\n";
- StringTokenizer st = new StringTokenizer(domainaddr," \t:,");
- while ( st.hasMoreTokens()) {
- sendBroadcast( hello, st.nextToken() );
- }
- }
-
- IvyWatcher(Ivy bus) throws IvyException {
- this.bus = bus;
- domainaddrList = new Vector();
+ // starts a Thread listening on the socket
+ watcherrunning=true;
+ broadcastListener = new Thread(this);
+ broadcastListener.start();
+ // notifies our arrival on each domain: protocol version + port
+ String hello = bus.PROCOCOLVERSION + " " + bus.applicationPort + "\n";
+ StringTokenizer st = new StringTokenizer(domainaddr," \t:,");
+ while ( st.hasMoreTokens()) { sendBroadcast( hello, st.nextToken() ); }
}
-
- public void run() {
- DatagramPacket packet;
- byte buf[] = new byte[256];
- int port;
- int version;
- /**/
- traceDebug("IvyWatcher waiting for Broadcast");
- while( bus.ivyRunning() )
- {
- try {
- /* receive antoher application port */
- packet = new DatagramPacket(buf, 256);
- broadcast.receive( packet );
- String msg = new String(packet.getData()) ;
- InetAddress remotehost = packet.getAddress();
- traceDebug("BUSWATCHER Receive Broadcast from:"+
- remotehost.getHostName()+
- ":"+packet.getPort()
- +"'"+msg+"'");
- // check if remoteaddr is in our broadcast domain list
- if ( !isInDomain( remotehost ) )
- continue;
- StringTokenizer st = new StringTokenizer(msg);
- if ( ! st.hasMoreTokens()) {
- System.err.println("Bad format "+msg);
- continue;
- }
- version = Integer.parseInt( st.nextToken() );
- if ( ! st.hasMoreTokens()) {
- System.err.println("Bad format "+msg);
- continue;
- }
- port = Integer.parseInt( st.nextToken() );
- // TODO: More checking here host port domain etc....
- if ( (bus.getApplicationPort() == port) )
- {
- continue;
- }
- traceDebug("BUSWATCHER Broadcast de "
- +packet.getAddress().getHostName()+":"+packet.getPort()+" port "+port);
- /* connect to the application */
- try {
- Socket socket = new Socket( remotehost, port );
- bus.addClient( socket );
- } catch ( UnknownHostException e ) {
- System.err.println("Unkonwn host "+remotehost);
- } catch ( IOException e) {
- System.err.println("can't connect to "+remotehost+" port "+port);
- }
-
- } catch ( IOException e ) {
- // TODO: comment distinger la fin normale de 'erreur
- /* fermeture de la socket arret normal */
- traceDebug("Error IvyWatcher exception: " + e.getMessage());
+
+ private boolean isInDomain( InetAddress host ){
+ byte rem_addr[] = host.getAddress();
+ for ( int i = 0 ; i < domainaddrList.size(); i++ ) {
+ byte addr[] = ((InetAddress)domainaddrList.elementAt(i)).getAddress();
+ int j ;
+ for ( j = 0 ; j < 4 ; j++ )
+ if ( (addr[j] != -1) && (addr[j] != rem_addr[j]) ) break;
+ if ( j == 4 ) {
+ traceDebug( "host " + host + " is in domain\n" );
+ return true;
+ }
}
- }// end while
+ traceDebug( "host " + host + " Not in domain\n" );
+ return false;
}
-
+
private void traceDebug(String s){
if (debug) System.out.println("-->ivywatcher<-- "+s);
}
- private boolean isInDomain( InetAddress host ){
- byte rem_addr[] = host.getAddress();
- for ( int i = 0 ; i < domainaddrList.size(); i++ )
- {
- byte addr[] = ((InetAddress)domainaddrList.elementAt(i)).getAddress();
- int j ;
- for ( j = 0 ; j < 4 ; j++ )
- if ( (addr[j] != -1) && (addr[j] != rem_addr[j]) ) break;
- if ( j == 4 )
- {
- traceDebug( "host " + host + " is in domain\n" );
- return true;
- }
- }
- traceDebug( "host " + host + " Not in domain\n" );
- return false;
- }
-}
+
+} // class IvyWatcher
+/* EOF */
diff --git a/src/Probe.java b/src/Probe.java
new file mode 100644
index 0000000..6218e03
--- /dev/null
+++ b/src/Probe.java
@@ -0,0 +1,77 @@
+package fr.dgac.ivy ;
+import gnu.getopt.Getopt;
+import java.io.*;
+
+/**
+ * terminal implementation in java of the ivyprobe.
+ * For a graphical version, see TestIvySwing
+ * @see fr.dgac.ivy.TestIvySwing
+ * @author Yannick Jestin
+ * @author <a href="http://www.tls.cena.fr/products/ivy/">http://www.tls.cena.fr/products/ivy/</a>
+ */
+class Probe implements IvyApplicationListener, IvyMessageListener {
+
+ public static void main(String[] args) {
+ Probe p = new Probe();
+ Getopt opt = new Getopt("Probe",args,"b:d");
+ int c;
+ String domain=Ivy.DEFAULT_DOMAIN;
+ while ((c = opt.getopt()) != -1) switch (c) {
+ case 'b':
+ domain=opt.getOptarg();
+ break;
+ case 'd':
+ System.setProperty("IVY_DEBUG","yes");
+ break;
+ default:
+ }
+ // connexion to the Bus
+ Ivy bus;
+ try {
+ bus=new Ivy("JPROBE","JPROBE ready",p);
+ for (int i=opt.getOptind();i<args.length;i++) {
+ System.out.println("you want to subscribe to " + args[i]);
+ bus.bindMsg(args[i],p);
+ }
+ System.out.println("broadcasting on "+domain);
+ bus.start(domain);
+ String s;
+ DataInputStream in =
+ new DataInputStream(new BufferedInputStream(System.in));
+ // infinite loop on keyboard input
+ while ((s=in.readLine()).length()!=0)
+ System.out.println("-> Sent to " +bus.sendMsg(s)+" peers");
+ } catch (IvyException ie) {
+ System.out.println("Caught an exception. quitting. "+ie.getMessage());
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void connect(IvyClient client) {
+ System.out.println(client.getApplicationName() + " connected " );
+ for (java.util.Enumeration e=client.getRegexps();e.hasMoreElements();)
+ System.out.println(client.getApplicationName() + " subscribes to "
+ +e.nextElement() );
+ }
+
+ public void disconnect(IvyClient client) {
+ System.out.println(client.getApplicationName() + " disconnected " );
+ }
+
+ public void die(IvyClient client, int id) {
+ System.out.println(client.getApplicationName() + " die "+ id );
+ }
+
+ public void directMessage(IvyClient client, int id, String arg) {
+ System.out.println(client.getApplicationName() + " direct Message "+ id + arg );
+ }
+
+ public void receive(IvyClient client, String[] args) {
+ String s=client.getApplicationName() + " sent";
+ for (int i=0;i<args.length;i++) s+=" '"+args[i]+"'";
+ System.out.println(s);
+ }
+
+} // class Probe
+// EOF
diff --git a/src/TestIvy.java b/src/TestIvy.java
index 46f484d..27a6d08 100755
--- a/src/TestIvy.java
+++ b/src/TestIvy.java
@@ -3,46 +3,62 @@ package fr.dgac.ivy ;
import java.awt.* ;
import java.awt.event.* ;
-class TestIvy extends Panel implements IvyApplicationListener{
+/**
+ * toy tool to probe the Ivy software bus.
+ * it relies on the AWT, and is less useable than TestIvySwing, which should
+ * be preferred.
+ *
+ * @see fr.dgac.ivy.TestIvySwing
+ * @author François-Régis Colin
+ * @author Yannick Jestin
+ * @author <a href="http://www.tls.cena.fr/products/ivy/">http://www.tls.cena.fr/products/ivy/</a>
+ */
+
+class TestIvy extends Panel implements IvyApplicationListener {
+ private Ivy bus ;
+ private String regexp="";
+ private TextField tfRegex, tfSend ;
+ private TextArea ta ;
+ private Button buApply, buSend, buClear ;
+ private java.text.SimpleDateFormat format = new
+ java.text.SimpleDateFormat("hh:mm:ss");
+ private int regexp_id;
- Ivy bus ;
- String regexp ;
- TextField tfRegex, tfSend ;
- TextArea ta ;
- Button buApply, buSend, buClear ;
- int regexp_id;
public TestIvy() throws IvyException {
super(new BorderLayout());
- regexp = "";
- // un textarea au centre
ta = new TextArea();
ta.setEditable(false);
add(ta,BorderLayout.CENTER);
- // une zone regex au nord
Panel p = new Panel(new BorderLayout());
p.add(new Label("Regex:"),BorderLayout.WEST);
tfRegex = new TextField(regexp);
tfRegex.addActionListener(new REGCB());
p.add(tfRegex,BorderLayout.CENTER);
add(p,BorderLayout.NORTH);
- // une zone messages au sud
p = new Panel(new BorderLayout());
p.add(new Label("Msg:"),BorderLayout.WEST);
tfSend = new TextField("");
tfSend.addActionListener(new SENDCB());
p.add(tfSend,BorderLayout.CENTER);
add(p,BorderLayout.SOUTH);
- bus = new Ivy("JAVA TESTBUS","Testbus ridi",this);
- bus.start(null);
- append( "Ivy Domain: "+ bus.getDomain(null) );
+ bus = new Ivy("JAVA TESTBUS","Testbus is ready",this);
+ bus.start(null);
+ append( "Ivy Domain: "+ bus.getDomain(null) );
+ }
+
+ public static void main(String[] args) throws IvyException {
+ TestIvy tb = new TestIvy();
+ Frame f = new Frame("TestIvy");
+ f.addWindowListener( tb.new WCCB(f,tb)) ;
+ f.add(tb, BorderLayout.CENTER);
+ f.pack();
+ f.setVisible(true);
}
- public void stop() {
- bus.stop();
- }
public void connect(IvyClient client) {
- append(client.getApplicationName() + " connected " );
+ append(client.getApplicationName() + " connected " );
}
+
public void disconnect(IvyClient client) {
append(client.getApplicationName() + " disconnected " );
}
@@ -52,64 +68,53 @@ class TestIvy extends Panel implements IvyApplicationListener{
public void directMessage(IvyClient client, int id, String arg) {
append(client.getApplicationName() + " direct Message "+ id + arg );
}
- /* inner */ class REGCB implements ActionListener, IvyMessageListener {
+
+ class REGCB implements ActionListener, IvyMessageListener {
public void actionPerformed(ActionEvent e) {
- // enlever l'ancienne regex
- bus.unBindMsg(regexp_id);
- // ajoute la nouvelle regex
- regexp=tfRegex.getText();
- regexp.trim();
+ try {
+ bus.unBindMsg(regexp_id);
+ } catch (IvyException ie) {
+ System.out.println("Big badaboum"); // this should not happen
+ }
+ regexp=tfRegex.getText();
+ regexp.trim();
regexp_id = bus.bindMsg(regexp,this);
- tfRegex.setText("");
+ tfRegex.setText("");
+ }
+ public void receive(IvyClient client, String[] args) {
+ String out="client " + client.getApplicationName() + " envoie: [ ";
+ for (int i=0;i<args.length;i++)
+ out=out+args[i]+ ((i<args.length-1)?" , ":"");
+ out = out + " ]";
+ append(out);
}
- public void receive(IvyClient client, String[] args) {
- String out="client " + client.getApplicationName() + " envoie: [ ";
- for (int i=0;i<args.length;i++)
- out=out+args[i]+ ((i<args.length-1)?" , ":"");
- out = out + " ]";
- append(out);
- }
}
- /* inner */ class SENDCB implements ActionListener {
+ class SENDCB implements ActionListener {
public void actionPerformed(ActionEvent e) {
- int count;
+ int count;
String tosend = tfSend.getText();
tfSend.setText("");
count = bus.sendMsg(tosend);
- append("Sending '" + tosend + "' count " + count );
-
+ append("Sending '" + tosend + "' count " + count );
}
}
private void append(String s) {
- // je mettrais bien la date, aussi.
- ta.append(s + "\n");
- }
-
- public static void main(String[] args) throws IvyException {
- TestIvy tb = new TestIvy();
- Frame f = new Frame("TestIvy");
- f.addWindowListener( tb.new WCCB(f,tb)) ;
- f.add(tb, BorderLayout.CENTER);
- f.pack();
- f.setVisible(true);
+ ta.append("[" + format.format(new java.util.Date()) + "] "+ s + "\n");
}
- /* inner */ class WCCB extends WindowAdapter {
+ class WCCB extends WindowAdapter {
private Frame f;
- public WCCB(Frame f, TestIvy b) {
- this.f=f;
- }
+ public WCCB(Frame f, TestIvy b) { this.f=f; }
public void windowClosing(WindowEvent e) {
f.setVisible(false);
bus.stop();
- bus = null;
+ bus = null;
f.dispose();
- System.exit(0);
+ System.exit(0);
}
}
-}
-
+} // class TestIvy
// EOF
diff --git a/src/TestIvySwing.java b/src/TestIvySwing.java
index a6d3777..82afe43 100644
--- a/src/TestIvySwing.java
+++ b/src/TestIvySwing.java
@@ -4,30 +4,59 @@ import java.awt.BorderLayout;
import java.awt.event.*;
import javax.swing.* ;
+/**
+ * toy tool to probe the Ivy software bus.
+ * it relies on the Swing toolkit, which is not standard on jdk1.1 platform.
+ * if yyou don't have jdk1.2 or swing, consider downloading it. You can also
+ * use TestIvy
+ *
+ * @see fr.dgac.ivy.TestIvy
+ * @author François-Régis Colin
+ * @author Yannick Jestin
+ * @author <a href="http://www.tls.cena.fr/products/ivy/">http://www.tls.cena.fr/products/ivy/</a>
+ */
class TestIvySwing extends JPanel implements IvyApplicationListener {
+
+ private static String version ="1.0.2";
private static int index;
private static int nbTIS=0;
- String localname;
-
- Ivy bus ;
- String regexp = "^AIRCRAFT:";;
- JLabel laRegex;
- JTextArea ta ;
- JTextField tfRegex, tfSend ;
- JButton buApply, buSend, buClear ;
- JComboBox ports;
- int regexp_id;
- REGCB reg;
-
- public TestIvySwing() throws IvyException {
+ private String localname;
+ private Ivy bus ;
+ private String regexp = "(.*)";
+ private JLabel laRegex;
+ private JTextArea ta ;
+ private JTextField tfRegex, tfSend ;
+ private JButton buApply, buSend, buClear ;
+ private JComboBox ports;
+ private int regexp_id;
+ private REGCB reg;
+ private java.text.SimpleDateFormat format = new
+ java.text.SimpleDateFormat("hh:mm:ss");
+ private static String[] startDomainList = {
+ "127.255.255.255:2010",
+ "10.192.36.255:2223",
+ "10.192.36.255:2333",
+ "10.192.36.255:2020",
+ "10.192.36.255:3110",
+ };
+ private static java.util.Vector domainList;
+
+ static {
+ // initialize the domainlist
+ domainList = new java.util.Vector();
+ for (int i = 0; i<startDomainList.length;i++)
+ domainList.add(startDomainList[i]);
+ }
+
+ public static void main(String[] args) throws IvyException {newTestIvy();}
+
+ private TestIvySwing() throws IvyException {
super(new BorderLayout());
nbTIS++;
- // un textarea au centre
ta = new JTextArea(25,40);
ta.setEditable(false);
add(new JScrollPane(ta),BorderLayout.CENTER);
- // une zone regex au nord
JPanel p = new JPanel(new BorderLayout());
p.add(new JLabel("Regex:"),BorderLayout.WEST);
tfRegex = new JTextField();
@@ -35,14 +64,12 @@ class TestIvySwing extends JPanel implements IvyApplicationListener {
p.add(tfRegex,BorderLayout.CENTER);
p.add(laRegex=new JLabel(regexp),BorderLayout.EAST);
add(p,BorderLayout.NORTH);
- // une zone messages au sud du nord, mais au nord du centre
JPanel p2 = new JPanel(new BorderLayout());
p2.add(new JLabel("Msg:"),BorderLayout.WEST);
tfSend = new JTextField("");
tfSend.addActionListener(new SENDCB());
p2.add(tfSend,BorderLayout.CENTER);
p.add(p2,BorderLayout.SOUTH);
- // au sud, je rajoute un bouton pour spawner un autre testeur
JButton tmpb ;
(p = new JPanel()).add(tmpb=new JButton("spawn"));
tmpb.addActionListener(new ActionListener() {
@@ -57,31 +84,25 @@ class TestIvySwing extends JPanel implements IvyApplicationListener {
ta.setText("");
}
});
-
ports=new JComboBox();
- ports.addItem("127.255.255.255:2010");
- ports.addItem("143.196.53.255:2010");
- ports.addItem("143.196.53.255:2020");
- ports.addItem("143.196.53.255:3110");
+ ports.setEditable(true);
+ for (java.util.Enumeration e=domainList.elements();e.hasMoreElements();) {
+ ports.addItem((String) e.nextElement());
+ }
ports.addActionListener(new ComboCB());
p.add(ports);
add(p,BorderLayout.SOUTH);
- // gestion du fovus clavier
tfRegex.setNextFocusableComponent(tfSend);
tfSend.setNextFocusableComponent(tfRegex);
tfSend.setRequestFocusEnabled(true);
- localname = "TestIvySwing ("+index+")";
+ localname = "TestIvySwing "+version+" ("+index+")";
index++;
- bus = new Ivy(localname,localname+" prêt",this);
+ bus = new Ivy(localname,localname+" ready",this);
regexp_id = bus.bindMsg(regexp,reg);
bus.start(null);
append( "Ivy Domain: "+ bus.getDomain(null) );
}
- public String getLocalname(){ return localname;}
-
- public void stop() { bus.stop(); }
-
public void connect(IvyClient client) {
append(client.getApplicationName() + " connected " );
}
@@ -98,23 +119,31 @@ class TestIvySwing extends JPanel implements IvyApplicationListener {
append(client.getApplicationName() + " direct Message "+ id + arg );
}
- /* inner */ class ComboCB implements ActionListener {
+ private class ComboCB implements ActionListener {
public void actionPerformed(ActionEvent e) {
String newDomain=(String)ports.getSelectedItem();
+ try {
+ append( "deconnexion from domain "+ bus.getDomain(null));
+ } catch ( IvyException ie ) {
+ System.err.println("deconnexion failed");
+ }
bus.stop();
try {
bus.start(newDomain);
+ append( "Ivy Domain: "+ newDomain );
} catch (IvyException ie ) {
System.err.println("auuuugh "+newDomain);
}
}
} // ComboCB
- /* inner */ class REGCB implements ActionListener, IvyMessageListener {
+ private class REGCB implements ActionListener, IvyMessageListener {
public void actionPerformed(ActionEvent e) {
- // enlever l'ancienne regex
- bus.unBindMsg(regexp_id);
- // ajoute la nouvelle regex
+ try {
+ bus.unBindMsg(regexp_id);
+ } catch (IvyException ie) {
+ System.out.println("big badaboum"); // this cannot happen
+ }
regexp=tfRegex.getText();
regexp.trim();
regexp_id = bus.bindMsg(regexp,this);
@@ -131,7 +160,7 @@ class TestIvySwing extends JPanel implements IvyApplicationListener {
}
}
- /* inner */ class SENDCB implements ActionListener {
+ private class SENDCB implements ActionListener {
public void actionPerformed(ActionEvent e) {
int count;
String tosend = tfSend.getText();
@@ -143,31 +172,30 @@ class TestIvySwing extends JPanel implements IvyApplicationListener {
}
}
- private void append(String s) { ta.append(s + "\n"); }
-
+ private void append(String s) {
+ ta.insert("[" + format.format(new java.util.Date()) + "] "+ s + "\n",0);
+ }
- public static void newTestIvy() throws IvyException {
+ private static void newTestIvy() throws IvyException {
TestIvySwing tb = new TestIvySwing();
- JFrame f = new JFrame(tb.getLocalname());
+ JFrame f = new JFrame(tb.localname);
f.addWindowListener( tb.new WCCB(f,tb)) ;
f.getContentPane().add(tb, BorderLayout.CENTER);
f.pack();
f.setVisible(true);
}
- /* inner */ class WCCB extends WindowAdapter {
+ private class WCCB extends WindowAdapter {
private JFrame f;
public WCCB(JFrame f, TestIvySwing b) { this.f=f; }
public void windowClosing(WindowEvent e) {
bus.stop();
f.dispose();
+ // I leave when the last TestIvySwing exits
if (--nbTIS == 0) System.exit(0);
- // je quitte l'appli au départ du dernier TIS
}
- public void windowActivated(WindowEvent e) { tfSend.grabFocus(); }
- } // WCCB
-
- public static void main(String[] args) throws IvyException { newTestIvy(); }
-}
+ public void windowActivated(WindowEvent e) {tfSend.grabFocus();}
+ }
+} // class TestIvySwing
// EOF