summaryrefslogtreecommitdiff
path: root/CSharp/Ivy/IvyPPC/Ivy.cs
diff options
context:
space:
mode:
authorfcolin2007-02-01 09:53:59 +0000
committerfcolin2007-02-01 09:53:59 +0000
commitfdad9a4b517a3ee2cc93c1a165400ce10c061158 (patch)
tree1b522b9604faf8a475400ccedbfa7421fcab6f39 /CSharp/Ivy/IvyPPC/Ivy.cs
parentc07ab0dd5fdfaa1d122b3fa112cc85a9c5baebb7 (diff)
downloadivy-csharp-fdad9a4b517a3ee2cc93c1a165400ce10c061158.zip
ivy-csharp-fdad9a4b517a3ee2cc93c1a165400ce10c061158.tar.gz
ivy-csharp-fdad9a4b517a3ee2cc93c1a165400ce10c061158.tar.bz2
ivy-csharp-fdad9a4b517a3ee2cc93c1a165400ce10c061158.tar.xz
Utilisateur : Fcolin Date : 19/05/03 Heure : 15:31 Créé Commentaire: (vss 1)
Diffstat (limited to 'CSharp/Ivy/IvyPPC/Ivy.cs')
-rw-r--r--CSharp/Ivy/IvyPPC/Ivy.cs621
1 files changed, 621 insertions, 0 deletions
diff --git a/CSharp/Ivy/IvyPPC/Ivy.cs b/CSharp/Ivy/IvyPPC/Ivy.cs
new file mode 100644
index 0000000..c681585
--- /dev/null
+++ b/CSharp/Ivy/IvyPPC/Ivy.cs
@@ -0,0 +1,621 @@
+/// <summary> a software bus package
+/// *
+/// </summary>
+/// <author> François-Régis Colin
+/// </author>
+/// <author> <a href="http://www.tls.cena.fr/products/ivy/">http://www.tls.cena.fr/products/ivy/</a>
+/// *
+/// <pre>
+/// Ivy bus = new Ivy("Dummy agent","ready");
+/// bus.bindMsg("(.*)",new IvyMessageHandler(myMessageListener));
+/// bus.start(null);
+/// </pre>
+///
+/// </author>
+namespace IvyBus
+{
+ using System;
+ using System.IO;
+ using System.Collections;
+ using System.Net;
+ using System.Net.Sockets;
+ using System.Threading;
+ using System.Configuration;
+
+ public class Ivy
+ {
+ /* Event handler */
+ public delegate void DirectMessageHandler (IvyClient app, int id, string arg );
+ public delegate void ClientConnectedHandler (IvyClient app);
+ public delegate void ClientDisconnectedHandler (IvyClient app);
+ public delegate bool DieHandler (IvyClient app, int id );
+
+ public delegate void MessageHandler (IvyClient app, string[] args );
+
+
+ /* Event */
+ public event ClientConnectedHandler clientConnected;
+ public event ClientDisconnectedHandler clientDisconnected;
+ public event DirectMessageHandler directMessageReceived;
+ public event DieHandler dieReceived;
+ public Hashtable IvyClients
+ {
+ get
+ {
+ return clients;
+ }
+
+ }
+ public bool Debug
+ {
+ get
+ {
+ return debug;
+ }
+
+ }
+ private String ClientNames
+ {
+ get
+ {
+ String s = appName + " clients are: ";
+ foreach (IvyClient client in clients.Values )
+ {
+ s += client.ApplicationName + " ";
+ }
+ return s;
+ }
+
+ }
+ public String AppName
+ {
+ get
+ {
+ return appName;
+ }
+
+ }
+
+ /// <summary> the name of the application on the bus
+ /// </summary>
+ internal String appName;
+ /// <summary> the protocol version number
+ /// </summary>
+ public const int PROCOCOLVERSION = 3;
+ /// <summary> the port for the UDP rendez vous, if none is supplied
+ /// </summary>
+ public const int DEFAULT_PORT = 2010;
+ /// <summary> the domain for the UDP rendez vous
+ /// </summary>
+ public static readonly String DEFAULT_DOMAIN = "127.255.255.255:" + DEFAULT_PORT;
+ /// <summary> the library version, useful for development purposes only, when java is
+ /// invoked with -DIVY_DEBUG
+ /// </summary>
+ public const String libVersion = "1.0.0";
+
+ private bool debug;
+ private static int serial = 0; /* an unique ID for each regexp */
+ private TcpListener app;
+ private ArrayList watchers;
+ private volatile Thread serverThread; // to ensure quick communication of the end
+ private Hashtable callbacks;
+ private Hashtable clients;
+ private String[] messages_classes = null;
+ private bool stopped = false;
+ internal int applicationPort; /* Application port number */
+ internal Hashtable regexp_out;
+ internal String ready_message = null;
+
+ public const int TIMEOUTLENGTH = 3000;
+
+ /// <summary> Readies the structures for the software bus connexion.
+ /// *
+ /// All the dirty work is done un the start() method
+ /// </summary>
+ /// <seealso cref=" #start
+ /// "/>
+ /// <param name="name">The name of your Ivy agent on the software bus
+ /// </param>
+ /// <param name="message">The hellow message you will send once ready
+ /// </param>
+ /// <param name="appcb">A callback handling the notification of connexions and
+ /// disconnections, may be null
+ ///
+ /// </param>
+ public Ivy(String name, String message)
+ {
+ callbacks = new Hashtable();
+ clients = new Hashtable();
+ regexp_out = new Hashtable();
+ appName = name;
+ ready_message = message;
+ String debug_str = ConfigurationSettings.AppSettings["IVY_DEBUG"];
+ if ( debug_str != null )
+ debug = bool.Parse(debug_str);
+ }
+
+ /// <summary> connects the Ivy bus to a domain or list of domains.
+ /// *
+ /// <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
+ /// *
+ /// </summary>
+ /// <param name="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.
+ /// *
+ ///
+ /// </param>
+ public void start(String domainbus)
+ {
+ if (domainbus == null)
+ domainbus = Environment.GetEnvironmentVariable("IVYBUS");
+ if (domainbus == null)
+ domainbus = DEFAULT_DOMAIN;
+ try
+ {
+ app = new TcpListener(0);
+ app.Start();
+ applicationPort = ((IPEndPoint) app.LocalEndpoint).Port;
+ }
+ catch (IOException e)
+ {
+ throw new IvyException("can't open TCP service socket " + e);
+ }
+ traceDebug("lib: " + libVersion + " protocol: " + PROCOCOLVERSION + " TCP service open on port " + applicationPort);
+ watchers = new ArrayList();
+
+ Domain[] d = parseDomains(domainbus);
+ // readies the rendezvous : an IvyWatcher (thread) per domain bus
+ for (int index = 0; index < d.Length; index++)
+ {
+ IvyWatcher watcher = new IvyWatcher(this, d[index].Domainaddr, d[index].Port);
+ watchers.Add(watcher);
+ }
+ serverThread = new Thread(new ThreadStart(this.Run));
+ serverThread.Start();
+ // sends the broadcasts and listen to incoming connexions
+ for (int i = 0; i < watchers.Count; i++)
+ {
+ ((IvyWatcher) watchers[i]).start();
+ }
+ }
+
+ internal Domain[] parseDomains(String domainbus)
+ {
+ String[] st = domainbus.Split(',');
+ Domain[] d = new Domain[st.Length];
+ for (int i = 0; i < st.Length; i++)
+ {
+ d[i] = new Domain(IvyWatcher.getDomain(st[i]), IvyWatcher.getPort(st[i]));
+ }
+ return d;
+ }
+
+ /// <summary> disconnects from the Ivy bus
+ /// </summary>
+ public void stop()
+ {
+ lock(this)
+ {
+ if (stopped)
+ return ;
+ stopped = true;
+ traceDebug("beginning stopping the bus");
+ try
+ {
+ // stopping the serverThread
+ Thread t = serverThread;
+ serverThread = null;
+ if (t != null)
+ t.Interrupt();
+ // The serverThread might be stopped even before having been created
+ if (app != null)
+ app.Stop();
+ // stopping the IvyWatchers
+ if ( watchers != null )
+ for (int i = 0; i < watchers.Count; i++)
+ {
+ ((IvyWatcher) watchers[i]).stop();
+ }
+ // stopping the remaining IvyClients
+ lock(clients)
+ {
+ foreach (IvyClient c in clients.Values )
+ {
+ c.close(true); // will notify the reading thread
+ //removeClient(c); already donne in the thread
+ }
+ }
+ }
+ catch (IOException e)
+ {
+ traceDebug("IOexception Stop ");
+ }
+ traceDebug("the bus should have stopped so far");
+ }
+ }
+
+
+ /// <summary> 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>
+ /// </summary>
+ /// <param name="message">A String which will be compared to the regular
+ /// expressions of the different clients
+ /// </param>
+ /// <returns>the number of messages actually sent
+ ///
+ /// </returns>
+ public int sendMsg(String message)
+ {
+ 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
+ foreach (IvyClient client in clients.Values )
+ {
+ count += client.sendMsg(message);
+ }
+ return count;
+ }
+
+ /// <summary> 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"
+ /// </summary>
+ /// <param name="regexp">a perl regular expression, groups are done with parenthesis
+ /// </param>
+ /// <param name="callback">any objects implementing the IvyMessageListener
+ /// interface, on the AWT/Swing framework
+ /// </param>
+ /// <returns>the id of the regular expression
+ ///
+ /// </returns>
+ public int bindMsg(String regexp, MessageHandler callback)
+ {
+ // creates a new binding (regexp,callback)
+ Int32 key = serial++;
+ regexp_out.Add( key, regexp);
+ callbacks.Add( key, callback);
+ // notifies the other clients this new regexp
+ foreach (IvyClient c in clients.Values )
+ {
+ c.sendRegexp(key, regexp);
+ }
+ return key;
+ }
+
+ /// <summary> unsubscribes a regular expression
+ /// *
+ /// </summary>
+ /// <param name="id">the id of the regular expression, returned when it was bound
+ ///
+ /// </param>
+ public void unBindMsg(int id)
+ {
+ if ((regexp_out[id] == null) || (callbacks[id] == null))
+ {
+ throw new IvyException("client wants to remove an unexistant regexp " + id);
+ }
+ foreach (IvyClient c in clients.Values )
+ {
+ c.delRegexp(id);
+ }
+ }
+
+ /// <summary> unsubscribes a regular expression
+ /// *
+ /// </summary>
+ /// <returns>a boolean, true if the regexp existed, false otherwise or
+ /// whenever an exception occured during unbinding
+ /// </returns>
+ /// <param name="String">the string for the regular expression
+ ///
+ /// </param>
+ public bool unBindMsg(String re)
+ {
+ foreach (Int32 k in regexp_out.Keys )
+ {
+ if (((String) regexp_out[k]).CompareTo(re) == 0)
+ {
+ try
+ {
+ unBindMsg(k);
+ }
+ catch (IvyException ie)
+ {
+ return false;
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+ internal void FireDirectMessage (IvyClient app, int id, string arg )
+ {
+ if ( directMessageReceived != null )
+ directMessageReceived( app, id, arg);
+ }
+ internal void FireClientConnected (IvyClient app)
+ {
+ if ( clientConnected != null )
+ clientConnected( app);
+ }
+ internal void FireClientDisconnected (IvyClient app)
+ {
+ if ( clientDisconnected != null )
+ clientDisconnected( app);
+ }
+ internal bool FireDie (IvyClient app, int id )
+ {
+ if ( dieReceived != null )
+ return dieReceived( app, id);
+ else return false;
+ }
+
+
+ /*
+ * removes a client from the list
+ */
+ internal void removeClient(IvyClient c)
+ {
+ lock(clients)
+ {
+ clients.Remove(c.ClientKey);
+ }
+ }
+
+
+
+ /// <summary> gives the names of IvyClient(s)
+ /// </summary>
+
+ /// <summary> gives a list of IvyClient(s) with the name given in parameter
+ /// *
+ /// </summary>
+ /// <param name="name">The name of the Ivy agent you're looking for
+ ///
+ /// </param>
+ public ArrayList getIvyClientsByName(String name)
+ {
+ ArrayList v = new ArrayList();
+ foreach (IvyClient ic in clients.Values )
+ {
+ if (((ic.ApplicationName).CompareTo(name)) == 0)
+ v.Add(ic);
+ }
+ return v;
+ }
+
+ /////////////////////////////////////////////////////////////////:
+ //
+ // Protected methods
+ //
+ /////////////////////////////////////////////////////////////////:
+
+ internal void addClient(MyTcpClient socket)
+ {
+ lock(this)
+ {
+ if (stopped)
+ return ;
+ IvyClient client = new IvyClient(this,socket);
+ clients.Add( client.ClientKey, client);
+ traceDebug(ClientNames);
+ }
+ }
+
+ internal void callCallback(IvyClient client, Int32 key, String[] tab)
+ {
+ MessageHandler callback = (MessageHandler) callbacks[key];
+ if (callback == null)
+ {
+ throw new IvyException("(callCallback) Not regexp matching id " + key);
+ }
+ callback(client, tab);
+ }
+
+ private static String[] myTokenize(String s, String separator)
+ {
+ int index = 0, last = 0, length = s.Length;
+ ArrayList v = new ArrayList();
+ if (length != 0)
+ while (true)
+ {
+ index = s.IndexOf(separator, last);
+ if (index == - 1)
+ {
+ v.Add(s.Substring(last, (length) - (last)));
+ break;
+ }
+ else if (index < s.Length)
+ {
+ v.Add(s.Substring(last, (index) - (last)));
+ last = index + 1;
+ }
+ else
+ {
+ break;
+ }
+ }
+ String[] tab = new String[v.Count];
+ v.CopyTo(tab);
+ return tab;
+ }
+
+ public static String getDomain(String domainbus)
+ {
+ if (domainbus == null)
+ {
+ domainbus = ConfigurationSettings.AppSettings["IVYBUS"];
+ }
+ if (domainbus == null)
+ {
+ domainbus = Environment.GetEnvironmentVariable("IVYBUS");
+ }
+ if (domainbus == null)
+ domainbus = DEFAULT_DOMAIN;
+ return domainbus;
+ }
+
+
+ /// <summary> checks the "validity" of a regular expression.
+ /// </summary>
+ internal bool CheckRegexp(String exp)
+ {
+ bool regexp_ok = true;
+ if (exp.StartsWith("^") && messages_classes != null)
+ {
+ regexp_ok = false;
+ for (int i = 0; i < messages_classes.Length; i++)
+ {
+ if (messages_classes[i].Equals(exp.Substring(1)))
+ return true;
+ }
+ }
+ return regexp_ok;
+ }
+
+ /*
+ * prevents two clients from connecting to each other at the same time
+ * there might still be a lingering bug here, that we could avoid with the
+ * SchizoToken.
+ */
+ internal bool checkConnected(IvyClient clnt)
+ {
+ if (clnt.AppPort == 0)
+ return false;
+ foreach (IvyClient client in clients.Values )
+ {
+ if (clnt != client && client.sameClient(clnt))
+ return true;
+ }
+ return false;
+ }
+
+ /*
+ * the service socket thread reader main loop
+ */
+ private void Run()
+ {
+ traceDebug("Ivy service Thread started");
+ bool running = true;
+ while (running)
+ {
+ try
+ {
+ Socket sock = app.AcceptSocket();
+ MyTcpClient socket = new MyTcpClient(sock);
+ if (stopped)
+ break;
+ // early disconnexion
+ addClient(socket); // the peer called me
+ }
+ catch (IOException e)
+ {
+
+ traceDebug("Error IvyServer exception: " + e.Message);
+ Console.Out.WriteLine("Ivy server socket reader caught an exception " + e.Message);
+ // e.printStackTrace();
+ }
+ catch (SocketException e)
+ {
+ traceDebug("my server socket has been closed");
+ running = false;
+ }
+ }
+ traceDebug("Ivy service Thread stopped");
+ }
+
+
+ private void traceDebug(String s)
+ {
+ if (debug)
+ Console.Out.WriteLine("-->ivy<-- " + s);
+ }
+
+ /* a small private method for debbugging purposes */
+
+ public String domains(String toparse)
+ {
+ String s = "broadcasting on ";
+ Ivy.Domain[] d = parseDomains(toparse);
+ for (int index = 0; index < d.Length; index++)
+ {
+ s += d[index].Domainaddr + ":" + d[index].Port + " ";
+ }
+ return s;
+ }
+
+ internal class Domain
+ {
+ public virtual String Domainaddr
+ {
+ get
+ {
+ return domainaddr;
+ }
+
+ }
+ public virtual int Port
+ {
+ get
+ {
+ return port;
+ }
+
+ }
+ private String domainaddr;
+ private int port;
+ public Domain( String domainaddr, int port)
+ {
+ this.domainaddr = domainaddr; this.port = port;
+ }
+ public override String ToString()
+ {
+ return domainaddr + ":" + port;
+ }
+ }
+
+
+ /*
+ * unitary test. Normally, running Ivy.Ivy should stop in 2.3 seconds :)
+ */
+ [STAThread]
+ public static void Main(String[] args)
+ {
+ Ivy bus = new Ivy("Test Unitaire", "TU ready");
+ try
+ {
+ bus.start(null);
+ try
+ {
+ Thread.Sleep(new TimeSpan(10000 * 2000));
+ }
+ catch (ThreadInterruptedException ie)
+ {
+ }
+ bus.stop();
+ }
+ catch (IvyException ie)
+ {
+ Console.Error.WriteLine( ie.Message );
+ Console.Error.WriteLine( ie.StackTrace );
+ }
+ }
+ }
+ // class Ivy
+
+ /* EOF */
+} \ No newline at end of file