aboutsummaryrefslogtreecommitdiff
path: root/src/Ivy.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/Ivy.java')
-rwxr-xr-xsrc/Ivy.java227
1 files changed, 151 insertions, 76 deletions
diff --git a/src/Ivy.java b/src/Ivy.java
index 08f3c6c..15926bc 100755
--- a/src/Ivy.java
+++ b/src/Ivy.java
@@ -10,8 +10,7 @@ package fr.dgac.ivy ;
import java.net.*;
import java.io.*;
-import java.util.Vector;
-import java.util.Hashtable;
+import java.util.*;
/**
* A class connecting to the Ivy software bus.
@@ -24,15 +23,22 @@ import java.util.Hashtable;
*
* CHANGELOG:
* 1.0.12:
+ * - setSoTimeout is back on the server socket
+ * - added a regression test main()
+ * - clients is now a Hashtable. the deletion now works better
+ * - getIvyClientsByName allows the research of IvyClient by name
+ * - getDomain doesnt throw IvyException anymore
* - removed the close() disconnect(IvyClient c). Fixes a big badaboum bug
* - getDomain becomes public
+ * - adding the sendToSelf feature
+ * - fixed the printStackTrace upon closing of the ServerSocket after a close()
*/
-public class Ivy implements Runnable, IvyApplicationListener {
+public class Ivy implements Runnable {
/**
* the name of the application on the bus
*/
- public String appName;
+ String appName;
/**
* the protocol version number
*/
@@ -49,22 +55,25 @@ public class Ivy implements Runnable, IvyApplicationListener {
* the library version, useful for development purposes only, when java is
* invoked with -DIVY_DEBUG
*/
- public static final String libVersion ="1.0.12";
+ public static final String libVersion ="1.2.0";
private boolean debug;
- private static int serial=0; /* an unique ID for each regexp */
+ private static int serial=0; /* an unique ID for each regexp */
+ private static int clientSerial=0; /* an unique ID for each IvyClient */
private ServerSocket app;
- private IvyWatcher watch;
- private Thread server;
+ private Vector watchers;
+ private volatile Thread serverThread; // to ensure quick communication of the end
private Hashtable callbacks = new Hashtable();
- private Vector clients = new Vector();
+ private Hashtable clients = new Hashtable();
private Vector ivyApplicationListenerList = new Vector();
private String messages_classes[] = null;
-
+ private boolean sendToSelf = false ;
+ private boolean stopped = false;
int applicationPort; /* Application port number */
- boolean ivyRunning = false;
Hashtable regexp_out = new Hashtable();
String ready_message = null;
+
+ public final static int TIMEOUTLENGTH = 3000;
/**
* Readies the structures for the software bus connexion.
@@ -86,8 +95,8 @@ public class Ivy implements Runnable, IvyApplicationListener {
/**
* connects the Ivy bus to a domain or list of domains.
*
- * <li>One thread (IvyWatcher) to watch rendezvous traffic (UDP or TCPMulticast)
- * <li>One thread (server/Ivy) to accept incoming connexions on server socket
+ * <li>One thread (IvyWatcher) for each traffic rendezvous (either UDP broadcast or TCPMulticast)
+ * <li>One thread (serverThread/Ivy) to accept incoming connexions on server socket
* <li>a thread for each IvyClient when the connexion has been done
*
* @param domainbus a domain of the form 10.0.0:1234, it is similar to the
@@ -100,41 +109,63 @@ public class Ivy implements Runnable, IvyApplicationListener {
public void start(String domainbus) throws IvyException {
try {
app = new ServerSocket(0);
+ app.setSoTimeout(TIMEOUTLENGTH);
applicationPort = app.getLocalPort();
} catch (IOException e) {
throw new IvyException("can't open TCP service socket " + e );
}
traceDebug("lib: "+libVersion+" protocol: "+PROCOCOLVERSION+" TCP service open on port "+applicationPort);
- watch = new IvyWatcher(this);
- ivyRunning = true;
- server = new Thread(this);
- server.start();
- watch.start(getDomain(domainbus));
+ watchers = new Vector();
+
+ // readies the rendezvous : an IvyWatcher (thread) per domain bus
+ StringTokenizer st = new StringTokenizer(domainbus,",");
+ while ( st.hasMoreTokens()) {
+ String s = st.nextToken() ;
+ String domainaddr=IvyWatcher.getDomain(s);
+ int port=IvyWatcher.getPort(s);
+ IvyWatcher watcher =new IvyWatcher(this,domainaddr,port);
+ watchers.addElement(watcher);
+ }
+ serverThread = new Thread(this);
+ serverThread.start();
+ // sends the broadcasts and listen to incoming connexions
+ for (int i=0;i<watchers.size();i++){ ((IvyWatcher)watchers.elementAt(i)).start(); }
}
/**
- * disconnects from the Ivy bus.
+ * disconnects from the Ivy bus
*/
public void stop() {
- if (!ivyRunning) {
- traceDebug("was already stropped ...");
- return;
- }
+ if (stopped ) return;
+ traceDebug("beginning stopping the bus");
try {
- ivyRunning = false;
+ // stopping the serverThread
+ Thread t=serverThread;
+ serverThread=null;
+ t.interrupt();
app.close();
- watch.stop();
- for (int i=0;i<clients.size();i++) {
- IvyClient client = (IvyClient)clients.elementAt(i);
- client.close("normal Ivy Stopping...");
+ // stopping the IvyWatchers
+ for (int i=0;i<watchers.size();i++){ ((IvyWatcher)watchers.elementAt(i)).stop(); }
+ // stopping the remaining IvyClients
+ for (Enumeration e=clients.elements();e.hasMoreElements();) {
+ IvyClient c = (IvyClient)e.nextElement();
+ c.close(true);
+ removeClient(c);
}
} catch (IOException e) {
traceDebug("IOexception Stop ");
}
- clients.removeAllElements();
+ traceDebug("the bus should have stopped so far");
+ stopped = true;
}
/**
+ * Toggles the sending of messages to oneself
+ *
+ */
+ public void sendToSelf(boolean b) {sendToSelf=b;}
+
+ /**
* 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
@@ -147,10 +178,13 @@ public class Ivy implements Runnable, IvyApplicationListener {
int count = 0;
// an alternate implementation would one sender thread per client
// instead of one for all the clients. It might be a performance issue
- for ( int i = 0 ; i < clients.size(); i++ ) {
- IvyClient client = (IvyClient)clients.elementAt(i);
+ for ( Enumeration e=clients.elements();e.hasMoreElements();) {
+ IvyClient client = (IvyClient)e.nextElement();
count += client.sendMsg( message );
}
+ if (sendToSelf) {
+ // TODO
+ }
return count;
}
@@ -176,8 +210,9 @@ public class Ivy implements Runnable, IvyApplicationListener {
regexp_out.put(key,regexp);
callbacks.put(key,callback );
// notifies the other clients this new regexp
- for (int i=0;i<clients.size();i++){
- ((IvyClient)clients.elementAt(i)).sendRegexp(key.intValue(),regexp);
+ for (Enumeration e=clients.elements();e.hasMoreElements();) {
+ IvyClient c = (IvyClient)e.nextElement();
+ c.sendRegexp(key.intValue(),regexp);
}
return key.intValue();
}
@@ -193,8 +228,8 @@ public class Ivy implements Runnable, IvyApplicationListener {
|| (callbacks.remove(key) == null ) ) {
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 );
+ for (Enumeration e=clients.elements();e.hasMoreElements();) {
+ ((IvyClient)e.nextElement()).delRegexp(id );
}
}
@@ -219,44 +254,67 @@ public class Ivy implements Runnable, IvyApplicationListener {
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
- */
+ /* invokes the application listeners upon arrival of a new Ivy client */
public void connect(IvyClient 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){
+ /* invokes the application listeners upon arrival of a new Ivy client */
+ void disconnectReceived(IvyClient 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
+ /*
+ * removes a client from the list
*/
- public void die(IvyClient client, int id){
- for ( int i = 0 ; i < ivyApplicationListenerList.size(); i++ ) {
- ((IvyApplicationListener)ivyApplicationListenerList.elementAt(i)).die(client, id);
+ void removeClient(IvyClient c) { clients.remove(c.getClientKey()); }
+
+ /**
+ * invokes the application listeners when we are summoned to die
+ * then stops
+ */
+ public void dieReceived(IvyClient client, int id){
+ for ( int i=0 ;i<ivyApplicationListenerList.size();i++ ) {
+ ((IvyApplicationListener)ivyApplicationListenerList.elementAt(i)).die(client,id);
}
- stop();
}
- /* invokes the direct message callbacks
- * ibid
- */
+ /* invokes the direct message callbacks */
public void directMessage( IvyClient client, int id,String msgarg ){
for ( int i = 0 ; i < ivyApplicationListenerList.size(); i++ ) {
((IvyApplicationListener)ivyApplicationListenerList.elementAt(i)).directMessage(client,id, msgarg);
}
}
+ /**
+ * gives the names of IvyClient(s)
+ */
+ public Vector getIvyClients() {
+ Vector v=new Vector();
+ for (Enumeration e=clients.elements();e.hasMoreElements();) {
+ v.add(e.nextElement());
+ }
+ return v;
+ }
+
+ /**
+ * gives a list of IvyClient(s) with the name given in parameter
+ *
+ * @param name The name of the Ivy agent you're looking for
+ */
+ public Vector getIvyClientsByName(String name) {
+ Vector v=new Vector();
+ for (Enumeration e=clients.elements();e.hasMoreElements();) {
+ IvyClient ic = (IvyClient)e.nextElement();
+ if ( ((ic.getApplicationName()).compareTo(name))==0 ) v.addElement(ic);
+ }
+ return v;
+ }
+
/////////////////////////////////////////////////////////////////:
//
// Protected methods
@@ -264,12 +322,10 @@ public class Ivy implements Runnable, IvyApplicationListener {
/////////////////////////////////////////////////////////////////:
void addClient(Socket socket,boolean peerCalling) throws IOException {
- IvyClient client = new IvyClient(this, socket,peerCalling);
- clients.addElement(client);
- }
-
- void removeClient( IvyClient client ) {
- clients.removeElement( client );
+ IvyClient client = new IvyClient(
+ this, socket,peerCalling,new Integer(clientSerial++));
+ clients.put(client.getClientKey(),client);
+ traceDebug(getClientNames());
}
void callCallback(IvyClient client, Integer key, String[] tab) throws IvyException {
@@ -300,8 +356,7 @@ public class Ivy implements Runnable, IvyApplicationListener {
return tab;
}
-
- public static String getDomain(String domainbus) throws IvyException {
+ public static String getDomain(String domainbus) {
if ( domainbus == null ) domainbus = System.getProperty("IVYBUS");
if ( domainbus == null ) domainbus = DEFAULT_DOMAIN;
return domainbus;
@@ -329,39 +384,59 @@ public class Ivy implements Runnable, IvyApplicationListener {
*/
boolean checkConnected( IvyClient clnt ) {
if ( clnt.getAppPort() == 0 ) return false;
- for ( int i = 0 ; i < clients.size(); i++ ) {
- IvyClient client = (IvyClient)clients.elementAt(i);
+ for (Enumeration e=clients.elements();e.hasMoreElements();) {
+ IvyClient client = (IvyClient)e.nextElement();
if ( clnt != client && client.sameClient( clnt ) ) return true;
}
return false;
}
/*
- * the service socket thread reader.
+ * the service socket thread reader main loop
*/
public void run() {
- // traceDebug("IvyServer beginning transmission");
- while(ivyRunning){
+ Thread thisThread=Thread.currentThread();
+ while(thisThread==serverThread){
try {
Socket socket = app.accept();
addClient(socket,true); // the peer called me
+ } catch (InterruptedIOException ie) {
+ if (thisThread!=serverThread) break;
} catch( IOException e ) {
- traceDebug("Error IvyServer exception: " + e.getMessage());
- System.out.println("DEBUG TCP socket reader caught an exception " + e.getMessage());
- e.printStackTrace();
+ if (serverThread==thisThread) {
+ traceDebug("Error IvyServer exception: " + e.getMessage());
+ System.out.println("Ivy server socket reader caught an exception " + e.getMessage());
+ e.printStackTrace();
+ } else { traceDebug("my server socket has been closed"); }
}
}
- // traceDebug("IvyServer end of transmission");
+ traceDebug("stopping the server Thread");
}
- /////////////////////////////////////////////////////////////////:
- //
- // Private methods
- //
- /////////////////////////////////////////////////////////////////:
- private void traceDebug(String s){
- if (debug) System.out.println("-->ivy<-- "+s);
+ private void traceDebug(String s){ if (debug) System.out.println("-->ivy<-- "+s); }
+
+ /* a small private method for debbugging purposes */
+ private String getClientNames() {
+ String s = appName+" clients are: ";
+ for (Enumeration e=clients.elements();e.hasMoreElements();){
+ s+=((IvyClient)e.nextElement()).getApplicationName()+" ";
+ }
+ return s;
+ }
+
+ /*
+ * unitary 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(DEFAULT_DOMAIN);
+ try { Thread.sleep(2000); } catch (InterruptedException ie) { }
+ bus.stop();
+ } catch (IvyException ie) {
+ ie.printStackTrace();
+ }
}
} // class Ivy