package fr.dgac.ivy ;
import java.lang.Thread;
import java.net.*;
import java.io.*;
import java.util.*;
import gnu.regexp.*;
/**
* A private Class for the the peers on the bus.
*
* @author François-Régis Colin
* @author Yannick Jestin
* @author http://www.tls.cena.fr/products/ivy/
*
* 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.
*/
public class IvyClient extends Thread {
/* 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 MESSAGE_TERMINATOR = "\n"; /* the next protocol will use \r */
final static String StartArg = "\u0002";/* begin of arguments */
final static String EndArg = "\u0003"; /* end of arguments */
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;
IvyClient(Ivy bus, Socket socket,boolean peerCalling) throws IOException {
appName = "Unknown";
appPort = 0;
this.bus = bus;
this.socket = socket;
this.peerCalling=peerCalling;
socket.setSoTimeout(100);
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = socket.getOutputStream();
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();
}
/**
* returns the name of the remote agent.
*/
public String getApplicationName() { return appName ; }
/**
* 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
*/
Enumeration getRegexps() { return regexp_text.elements(); }
int getAppPort() { return appPort ; }
/* 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,"");}
/**
* 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();
RE regexp = (RE)regexp_in.get(key);
REMatch result = regexp.getMatch(message);
if ( result != null ) {
send(Msg,key,regexp.getNumSubs(),result);
count++;
}
}
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);
gardefou=false;
socket.close(); // should I ?
}
/**
* 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() ) ;
}
/**
* the code of the thread handling the incoming messages.
*/
public void run() {
String msg = null;
try {
traceDebug("Connected from "+ socket.getInetAddress().getHostName()+ ":"+socket.getPort());
while ( gardefou ) {
try {
if ((msg=in.readLine()) != null ) {
newParseMsg(msg);
}
} catch (IvyException ie) {
ie.printStackTrace();
} catch (InterruptedIOException ioe) {
// okay, nothing on the line
// do nothing but loop. It might be a bit resource-eating ...
}
}
traceDebug("normally Disconnected from "+ socket.getInetAddress().getHostName()+":"+socket.getPort());
socket.close();
out.close();
in.close();
} catch (IOException e) {
traceDebug("abnormally Disconnected from "+
socket.getInetAddress().getHostName()+":"+socket.getPort());
}
bus.disconnect(this);
bus.removeClient(this);
}
private void sendBuffer( String buffer ) throws IvyException {
buffer += "\n";
try {
out.write(buffer.getBytes() );
out.flush();
} catch ( IOException e ) {
throw new IvyException("IvyClient.sendBuffer.write failed: "+e.getMessage());
}
}
private void send(int type, int id, String arg) {
try {
sendBuffer(type+" "+id+StartArg+arg);
} catch (IvyException ie ) {
// TODO shoud fix the exception Handling here ...
System.err.println("received an exception: " + ie.getMessage());
ie.printStackTrace();
}
}
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.getStartIndex(sub) > -1) {
buffer += result.toString(sub)+EndArg;
}
}
try {
sendBuffer(buffer);
} catch (IvyException ie ) {
// TODO shoud fix the exception Handling here ...
System.err.println("received an exception: " + ie.getMessage());
ie.printStackTrace();
}
}
private String dumpHex(String s) {
byte[] b = s.getBytes();
String out = "";
String zu = "\t";
for (int i=0;i15) ? c : 'X')+" ";
}
out += zu;
return out;
}
private String dumpMsg(String s) {
String deb = " \""+s+"\" "+s.length()+" cars, ";
for (int i=0;i=b.length) throw new IvyException("protocol error");
try {
msgType = Integer.parseInt(s.substring(from,to));
} catch (NumberFormatException nfe) {
throw new IvyException("protocol error on msgType");
}
from=to+1;
while ((to=b.length) throw new IvyException("protocol error");
try {
msgId = new Integer(s.substring(from,to));
} catch (NumberFormatException nfe) {
throw new IvyException("protocol error on identifier");
}
from=to+1;
switch (msgType) {
case Bye:
bus.die(this,msgId.intValue());
gardefou=false;
break;
case AddRegexp:
String regexp=s.substring(from,b.length);
if ( bus.CheckRegexp(regexp) ) {
try {
regexp_in.put(msgId,new RE(regexp));
regexp_text.put(msgId,regexp);
} catch (REException e) {
throw new IvyException("regexp error " +e.getMessage());
}
} else {
throw new IvyException("regexp Warning exp='"+regexp+"' can't match removing from "+appName);
}
break;
case DelRegexp:
regexp_in.remove(msgId);
regexp_text.remove(msgId);
break;
case EndRegexp:
bus.connect(this);
/* TODO ?
* the peer is perhaps not ready to handle this message
* an assymetric processing should be written
*/
if (bus.ready_message!=null) sendMsg(bus.ready_message);
break;
case Msg:
Vector v = new Vector();
while (toIvyClient "+appName+"<-- "+s);
}
} // class IvyClient
/* EOF */