aboutsummaryrefslogtreecommitdiff
path: root/src/IvyClient.java
blob: 7ec220faccfa1a856d521b3592d19280025ccabd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
/*
** IvyClient
*/

package fr.dgac.ivy ;

import java.lang.Thread;
import java.net.*;
import java.io.*;
import java.util.*;
import gnu.regexp.*;

public 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;

  /*
   * gestion du protocole ping
   * ne marche pas encore
   */
  private long lastRead  = 0;
  private long pingDate = 0 ;
  private long pingLag =0 ;
  
  private Ivy bus;
  private Socket socket;
  private BufferedReader in;
  private OutputStream out;
  private Hashtable regexp_in = new Hashtable();

  static private boolean debug = (System.getProperty("IVY_DEBUG")!=null) ;

  IvyClient( Ivy bus, Socket socket ) throws IOException {
    appName = "Unkown";
    appPort = 0;
    this.bus = bus;
    this.socket = socket;
    lastRead = (new Date()).getTime();
    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");
    }
  }

  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,""); }

  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
   */
  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, "");
  }

  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  );
  }

  public 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;
  }
  
  void close(String msg) throws IOException {
    traceDebug( msg );
    socket.close();
    //stop();
}

  public boolean sameClient( IvyClient clnt ) {
    return ( appPort != 0 && appPort == clnt.appPort )
      && ( getRemoteAddress() == clnt.getRemoteAddress() ) ;
  }

  public void delRegexp( int id ) { send( DelRegexp,id,"");}

  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 */
	String token = null;
        /*
	 * ETAPE UN : extraire le message
	 */
        lastRead = (new Date()).getTime();
        StringTokenizer st = new StringTokenizer(msg);
        if  (!st.hasMoreTokens()){
          close("Bad format no type '"+msg+"'");
          break;
        }
        token=st.nextToken();
        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));
	/*
	 * 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
	 */
	String msgarg="";
        if (st.hasMoreTokens()) msgarg=st.nextToken("\n");

        /*
	 *ETAPE DEUX : traitement adapté au type de message
	 */
        switch (msgtype) {
        case Bye: break;
        case AddRegexp:
          if ( bus.CheckRegexp(msgarg) ) {
            try {
	      regexp_in.put(msgid,new RE(msgarg));
            } catch (REException e) {
              System.err.println("Bad pattern: "+e.getMessage());
            }
          } else {
	    System.err.println(
	     "Warning exp='"+msgarg+"' can't match removing from "+appName);
	  }
          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
	   */
          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);
	  break;
	case Pong:
	  // calcul du lag en millisecondes
          traceDebug("Pong Message");
	  pingLag = (new Date()).getTime() - pingDate ;
	  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 "+
        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 traceDebug(String s){
    if (debug) System.out.println("-->ivyclient<-- "+s);
  }
} // class IvyClient
/* EOF */