aboutsummaryrefslogtreecommitdiff
path: root/src/Ivy.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/Ivy.java')
-rwxr-xr-xsrc/Ivy.java362
1 files changed, 236 insertions, 126 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
+}