aboutsummaryrefslogtreecommitdiff
path: root/src/ProxyClient.java
blob: e6e8606f15b710e29d01f3348f2d8ed8f29f5998 (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
/**
 * ProxyClient: Ivy relay, first attempt, still in beta stage DO NOT USE
 *
 * @author	Yannick Jestin
 * @author	<a href="http://www.tls.cena.fr/products/ivy/">http://www.tls.cena.fr/products/ivy/</a>
 *
 * (c) ENAC
 *
 * changelog:
 *  1.2.14
 *    - remove the Thread.start() from the constructor, to avoid mulithread issues
 *  	see *  	http://findbugs.sourceforge.net/bugDescriptions.html#SC_START_IN_CTOR
 *  	now ,we have to call IvyClient.start() after it has been created
 *    - throws RuntimeException instead of System.exit(), allows code reuse
 *    - switch from gnu regexp (deprecated) to the built in java regexp
 *    - add generic types to declarations
 *   1.2.13
 *    - adds support for RESyntaxException
 *
 */
package fr.dgac.ivy ;
import java.io.*;
import java.net.*;
import java.util.HashMap ;
import java.util.Map ;
import java.util.Properties ;
import gnu.getopt.Getopt;
import java.util.regex.*;

class ProxyClient extends Ivy {

  private Socket clientSocket;
  private PrintWriter out;
  private BufferedReader in;
  private static boolean debug = (System.getProperty("IVY_DEBUG")!=null) ;
  private volatile Thread clientThread;		// volatile to ensure the quick communication
  private Map<String,String> id=new HashMap<String,String>();
  private Map<String,Ghost>ghosts = new HashMap<String,Ghost>();
  private Map<String,Puppet> puppets =new HashMap<String,Puppet>(); // key=id value=Puppet
  String domain=null;

  public static final int DEFAULT_SERVICE_PORT = 3456 ;
  public static final String DEFAULTNAME = "ProxyClient";
  public static final String helpmsg = "usage: java fr.dgac.ivy.ProxyClient [options] hostname\n\t-b BUS\tspecifies the Ivy bus domain\n\t-p\tport number, default "+DEFAULT_SERVICE_PORT+"\n\t-n ivyname (default "+DEFAULTNAME+")\n\t-q\tquiet, no tty output\n\t-d\tdebug\n\t-h\thelp\ncontacts the ProxyMaster on hostname\n";

  private static String name = DEFAULTNAME;
  public static void main(String[] args) {
    Ivy bus;
    Getopt opt = new Getopt("ProxyClient",args,"n:b:dqp:h");
    int c;
    int servicePort = DEFAULT_SERVICE_PORT;
    boolean quiet = false;
    String domain=null;
    while ((c = opt.getopt()) != -1) switch (c) {
    case 'n':
      name=opt.getOptarg();
      break;
    case 'b':
      domain=opt.getOptarg();
      break;
    case 'q':
      quiet=true;
      break;
    case 'd':
      Properties sysProp = System.getProperties();
      sysProp.put("IVY_DEBUG","yes");
      break;
    case 'p':
      String s="";
      try {
        servicePort = Integer.parseInt(s=opt.getOptarg());
      } catch (NumberFormatException nfe) {
        System.out.println("Invalid port number: " + s );
	throw new RuntimeException();
      }
      break;
    case 'h':
    default:
      System.out.println(helpmsg);
      return ;
    }
    String hostname="localhost";
    try {
      new ProxyClient(hostname,servicePort,domain).start();
    } catch (IvyException ie) {
      System.out.println("error, can't connect to Ivy");
      ie.printStackTrace();
      throw new RuntimeException();
    } catch (IOException ioe) {
      System.out.println("error, can't connect to the proxy master");
      ioe.printStackTrace();
      throw new RuntimeException();
    }
  }

  public ProxyClient(String hostname,int servicePort,String domain) throws IOException, IvyException {
    super(name,name+" ready",null); // I will join the bus
    System.out.println("PC contacting tcp:"+hostname+":"+servicePort);
    clientSocket = new Socket(hostname,servicePort) ; // contacting hostname:servicePort
    in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
    out = new PrintWriter(new OutputStreamWriter(clientSocket.getOutputStream()));
    clientThread=new Thread(new Servicer()); //
    this.domain=domain;
  }

  protected void start() throws IvyException {
    clientThread.start();
    start(domain);
    send("Hello bus="+domain);
  }
  
  static Pattern getId,fwdGhost,fwdPuppet,puppetRe;

  static {
    try {
      getId=Pattern.compile("^ID id=(.*) value=(.*)");
      fwdGhost=Pattern.compile("^ForwardGhost id=(.*) buffer=(.*)");
      fwdPuppet=Pattern.compile("^ForwardPuppet id=(.*) buffer=(.*)");
      puppetRe=Pattern.compile("^CreatePuppet id=(.*)");
    } catch ( PatternSyntaxException res ) {
      res.printStackTrace();
      System.out.println("Regular Expression bug in Ivy source code ... bailing out");
      throw new RuntimeException();
    }
  }

  void parseMsg(String msg) {
    // System.out.println("PC parsing "+msg);
    Matcher m;
    if ((m=getId.matcher(msg)).matches()) {
      id.put(m.group(1),m.group(2));
    } else if ((m=puppetRe.matcher(msg)).matches()) { // I must create a puppet
      String puppetId = m.group(1);
      puppets.put(puppetId,new Puppet(this,puppetId,domain));
    } else if ((m=fwdGhost.matcher(msg)).matches()) { // I must forward to the ghost
      Ghost g = ghosts.get(m.group(1));
      try { g.sendBuffer(m.group(2)); } catch( IvyException ie) { ie.printStackTrace(); }
    } else if ((m=fwdPuppet.matcher(msg)).matches()) { // I must forward to the puppet
      Puppet p = puppets.get(m.group(1));
      try { p.parse(m.group(2)); } catch( IvyException ie) { ie.printStackTrace(); }
    } else {
      System.out.println("unknown message "+msg);
    }
  }

  class Servicer implements Runnable {
    public void run() {
      //Thread thisThread = Thread.currentThread();
      traceDebug("Thread started");
      String msg;
      try {
        while (true) {
          msg=in.readLine();
          if (msg==null) break;
	  parseMsg(msg);
        }
      } catch (IOException ioe) {
        traceDebug("Subreader exception ...");
        ioe.printStackTrace();
	throw new RuntimeException();
      }
      traceDebug("Subreader Thread stopped");
      System.out.println("connexion to ProxyMaster lost");
      for (Puppet p:puppets.values()) p.stop();
      stop(); // leave the bus TODO: make a disconnexion/reconnexion possible ?
    }
  }

  void send(String s) { // sends a message to the proxyMaster
    out.println(s);
    out.flush();
  }

  /*
   * here, I create a ghost instead of a normal Ivy Client, to catch the
   * protocol and forward everything to the proxies.
   * TODO: remember everything in case a new proxy client comes ?
   */
  protected boolean createIvyClient(Socket s,int port, boolean domachin) throws IOException {
    IvyClient i;
    // TODO si c'est un puppet, je ne dois pas creer de Ghost
    // voir meme me deconnecter du biniou ?
    for (Puppet p:puppets.values()) {
      if (( p.bus.getAP() == port ) && !domachin ) {
        // this new Ivy agent is in fact one of my puppets ...
	System.out.println("not Ghosting this (probable) Puppet Ivy agent");
	i= new IvyClient(this,s,port,domachin);
	p.bus.getPool().execute(i);
	return true;
      }
    }
    String key = getWBUId();
    String ghostId;
    send("GetID id="+key); // asks a centralized ID from ProxyMaster
    try { // waits for the answer
      while ((ghostId=id.get(key))==null) { Thread.sleep(200); }
      Ghost g = new Ghost(this,s,port,domachin,ghostId,this);
      ghosts.put(ghostId,g);
      return true;
    } catch (InterruptedException ie) { ie.printStackTrace(); }
    System.out.println("error waiting");
    throw new RuntimeException();
  }

  /*
   * the function to forward the protocol to all the Puppets through the
   * ProxyMaster
   *
   */
  protected void forwardPuppet(String id,String buffer) {
    out.println("ForwardPuppet id="+id+" buffer="+buffer);
    out.flush();
  }

  private static void traceDebug(String s){
    if (debug) System.out.println("-->ProxyClient "+name+"<-- "+s);
  }

  

}