Source code: org/fudaa/dodico/objet/CExec.java
1 /*
2 * @file CExec.java
3 * @creation 1999-07-21
4 * @modification $Date: 2003/01/21 14:36:50 $
5 * @license GNU General Public License 2
6 * @copyright (c)1998-2001 CETMEF 2 bd Gambetta F-60231 Compiegne
7 * @mail devel@fudaa.org
8 */
9
10 package org.fudaa.dodico.objet;
11
12 import java.io.BufferedReader;
13 import java.io.IOException;
14 import java.io.InputStreamReader;
15 import java.io.PrintStream;
16
17 import org.fudaa.dodico.commun.DodicoLib;
18
19 /**
20 * Un lanceur de processus externes.
21 * Lance la commande contenue dans le tableau de String[] dans un nouveau
22 * process. 2 Threads autres sont lances
23 * pour lire les flux de sortie standard et d'erreur. Ils sont arretes
24 * a la fin du process. Ces threads sont obligatoire pour vider les buffers
25 * de sorties du processus ( surtout sous windows). Il se peut qu'un thread
26 * soit bloque : si un flux n'est pas ouvert la methode readline() reste
27 * bloqué. Dans ce cas, le thread concerné sera interrompu.
28 *
29 * @version $Id: CExec.java,v 1.10 2003/01/21 14:36:50 deniger Exp $
30 * @author Axel von Arnim
31 */
32 public class CExec
33 {
34 String[] cmd;
35 PrintStream out_, err_;
36 private Process p;
37 boolean catchE;
38 MyReaderThread thOut_, thErr_;
39
40 /**
41 * Commandes a null et les exceptions seront catchés et ecrites sur la sortie
42 * d'erreur.
43 */
44 public CExec()
45 {
46 cmd = null;
47 catchE = true;
48
49 }
50
51 /**
52 * @param les parametres du processus a lancer.
53 * @see java.lang.Runtime#exec(String[])
54 */
55 public CExec(String[] _cmd)
56 {
57 this();
58 setCommand(_cmd);
59 }
60
61 /**
62 * Initialisation des commandes.
63 */
64 public void setCommand(String[] _cmd)
65 {
66 cmd = _cmd;
67 }
68
69 /**
70 * Definition du flux recuperant la sortie standard.
71 */
72 public void setOutStream(PrintStream _out)
73 {
74 //out.setStream(_out);
75 out_ = _out;
76 }
77
78 /**
79 * Definition du flux recuperant la sortie d'erreur.
80 */
81 public void setErrStream(PrintStream _err)
82 {
83 // err.setStream(_err);
84 err_ = _err;
85 }
86
87 /**
88 * Retourne le processus d'execution.
89 */
90 public Process getProcess()
91 {
92 return p;
93 }
94
95 /**
96 * Specifie si les exceptions doivent etre "catches" ou non.
97 * Si elles sont catchees une trace est laissée sur la sortie d'erreur.
98 */
99 public void setCatchExceptions(boolean _catchE)
100 {
101 catchE = _catchE;
102 }
103
104 /**
105 * Permet de fermer les threads de lecture apres l'arret du process.
106 * Si le thread est bloqué, il est
107 * interrompu. Sinon, s'il n'est pas fermé, on attend 50 ms avant de
108 * l'interrompre. Normalement les thread de lecture doivent s'arreter par
109 * eux-meme avant la fin du process.
110 */
111 private void closeReaderThread(MyReaderThread _th) throws SecurityException
112 {
113 try
114 {
115 if (_th == null)
116 return;
117 if (!_th.isAlive())
118 return;
119 //il est encore vivant, on attend
120 _th.setPriority(Thread.MAX_PRIORITY - 2);
121 //sous windows, il arrivre que la lecture du flux soit bloquee.
122 if (_th.isBlocked())
123 {
124 // _th.interrupt();
125 if ((DodicoLib.DEBUG) && (err_ != null))
126 {
127 err_.println(_th.getName() + " " + DodicoLib.geti18n("bloqué "));
128 }
129
130 }
131 else
132 {
133 //le thread n'est pas termine... bizarre
134 if ((DodicoLib.DEBUG) && (err_ != null))
135 {
136 err_.println(
137 "!!" + _th.getName() + " " + DodicoLib.geti18n("non terminé."));
138 }
139 //on a attendu 2 s, on interrompt le thread
140
141 try
142 {
143 if (_th != null)
144 _th.join(2000);
145 }
146 catch (InterruptedException _e)
147 {
148 }
149 }
150 //on a attendu 50ms, on interrompt le thread
151 if (_th.isAlive())
152 {
153 _th.interrupt();
154 if ((DodicoLib.DEBUG) && (err_ != null))
155 err_.println(
156 _th.getName() + " " + DodicoLib.geti18n("interrompu") + " ok");
157 }
158 }
159 catch (SecurityException _e)
160 {
161 System.err.println(_e.getMessage());
162 }
163 }
164
165 /**
166 * Cette commande stoppe le processus puis les 2 threads de lecture de flux.
167 * NON TESTEE.
168 */
169 public void stop()
170 {
171 if (p != null)
172 {
173 p.destroy();
174 }
175 closeReaderThread(thOut_);
176 closeReaderThread(thErr_);
177 }
178
179 /**
180 * Lance la command dans une process. 2 Threads sont lances
181 * pour lire les flux de sortie standard et d'erreur. Ils sont arretes
182 * a la fin du process.
183 */
184 public void exec()
185 {
186 if ((cmd == null) || (cmd.length <= 0))
187 {
188 if (err_ != null)
189 err_.println(DodicoLib.geti18n("Commande nulle"));
190 return;
191 }
192 BufferedReader psIn = null;
193 BufferedReader psErr = null;
194 int oldpriority = Thread.currentThread().getPriority();
195 try
196 {
197 if (err_ != null)
198 err_.println(
199 "***** " + cmd[0] + " " + DodicoLib.geti18n("lance") + " *****");
200 Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
201 if (DodicoLib.DEBUG)
202 System.out.println("lancement processus");
203 p = Runtime.getRuntime().exec(cmd);
204 psIn = new BufferedReader(new InputStreamReader(p.getInputStream()));
205 psErr = new BufferedReader(new InputStreamReader(p.getErrorStream()));
206 thErr_ = new MyReaderThread(err_, psErr);
207 thErr_.setName(DodicoLib.geti18n("Sortie d'erreur"));
208 thOut_ = new MyReaderThread(out_, psIn);
209 thOut_.setName(DodicoLib.geti18n("Sortie standard"));
210 try
211 {
212 Thread.currentThread().sleep(1000);
213 }
214 catch (InterruptedException e)
215 {
216 }
217 thOut_.start();
218 thErr_.start();
219 boolean interr = true;
220 while (interr)
221 {
222 try
223 {
224 p.waitFor();
225 interr = false;
226 }
227 catch (InterruptedException e)
228 {
229 interr = true;
230 }
231 }
232 if (DodicoLib.DEBUG)
233 {
234 System.out.println("fin processus");
235 System.out.println("fermeture sortie erreur....");
236 }
237 closeReaderThread(thErr_);
238 if (DodicoLib.DEBUG)
239 {
240 System.out.println("sortie erreur fermee");
241 System.out.println("fermeture sortie standard....");
242 }
243 closeReaderThread(thOut_);
244 if (DodicoLib.DEBUG)
245 System.out.println("sortie standard fermée....");
246 if (p.exitValue() != 0)
247 throw new RuntimeException(cmd[0] + ": abnormal exit");
248 if (err_ != null)
249 err_.println(
250 "***** " + cmd[0] + " " + DodicoLib.geti18n("termine") + " *****");
251 }
252 catch (IOException io)
253 {
254 if (catchE)
255 System.err.println(io);
256 else
257 throw new RuntimeException(io.getMessage());
258 }
259 catch (RuntimeException r)
260 {
261 if (catchE)
262 System.err.println(r);
263 else
264 throw r;
265 }
266 finally
267 {
268 try
269 {
270 if (DodicoLib.DEBUG)
271 System.out.println("fermeture sortie standard");
272 if (psIn != null)
273 psIn.close();
274 if (DodicoLib.DEBUG)
275 System.out.println("sortie standard fermee");
276 }
277 catch (Exception io)
278 {
279 if (catchE)
280 System.err.println("Severe " + io);
281 else
282 throw new RuntimeException("Severe " + io.getMessage());
283 }
284 try
285 {
286 if (DodicoLib.DEBUG)
287 System.out.println("fermeture sortie erreur");
288 if (psErr != null)
289 psErr.close();
290 if (DodicoLib.DEBUG)
291 System.out.println("sortie erreur fermee");
292 Thread.currentThread().setPriority(oldpriority);
293 }
294 catch (Exception io)
295 {
296 if (catchE)
297 System.err.println("Severe " + io);
298 else
299 throw new RuntimeException("Severe " + io.getMessage());
300 }
301 }
302 if (DodicoLib.DEBUG)
303 System.out.println("CExec.exec() terminee");
304 }
305
306 /**
307 * Prend les arguments du type ex=cmd1 ex=cmd2 et lance
308 * CExec.
309 */
310 public static void main(String[] args)
311 {
312 if (args == null)
313 return;
314 int n = args.length;
315 String[] execArg = new String[n];
316 String prefix = "ex=";
317 int prefL = prefix.length();
318 int index = 0;
319 String temp;
320 for (int i = 0; i < n; i++)
321 {
322 temp = args[i];
323 if (temp.startsWith(prefix))
324 {
325 execArg[index++] = temp.substring(prefL);
326 }
327 }
328 if (index == 0)
329 {
330 System.out.println("pas d'argument trouvé");
331 System.out.println("syntaxe ex=arg1 ex=arg2 ex=arg3 ....");
332 }
333 if (index != n)
334 {
335 String[] execArgTemp = new String[index];
336 System.arraycopy(execArg, 0, execArgTemp, 0, index);
337 execArg = execArgTemp;
338 }
339 CExec c = new CExec(execArg);
340 c.setErrStream(System.err);
341 c.setOutStream(System.out);
342 c.exec();
343 if (DodicoLib.DEBUG)
344 System.out.println("exec terminé");
345 }
346 }
347
348 /**
349 * Thread qui permet de lire les donnees d'un BufferReader et de les ecrire
350 * dans un PrintStream. Il se peut que ce thread soit bloqué si le flux d'entree
351 * du BufferedReader ne soit jamais initialise
352 * @see isBlocked();
353 */
354 class MyReaderThread extends Thread
355 {
356 private PrintStream str_;
357 private BufferedReader br_;
358 private boolean blocked_;
359
360 /**
361 *
362 * @param _str le flux dans lequel les donnees du buffer seront ecrites
363 * @param _br le buffer lu ( en general un buffer de sortie de process).
364 */
365 public MyReaderThread(PrintStream _str, BufferedReader _br)
366 {
367 super();
368 blocked_ = true;
369 str_ = _str;
370 br_ = _br;
371 setPriority(Thread.MAX_PRIORITY - 2);
372 }
373
374 /**
375 * Si le thread est lance et si cette commande renvoie true, cela signifie
376 * que le thread est bloque sur la premiere commande readLine. Dans ce cas,
377 * il est conseille d'interrompre le thread.
378 */
379 public boolean isBlocked()
380 {
381 return blocked_;
382 }
383
384 /**
385 * Lancement du thread de lecture: tant que le buffer contient des lignes
386 * elles sont renvoyees dans le PrintStream.
387 */
388 public void run()
389 {
390 String line = null;
391 try
392 {
393 blocked_ = true;
394 //sous windows le thread de lecture peut bloquer.
395 //On attend que le buffer puisse etre lu.
396 while (!br_.ready())
397 {
398 try
399 {
400 this.sleep(50);
401 }
402 catch (InterruptedException e)
403 {
404 }
405 }
406
407 blocked_ = false;
408 if (br_.ready())
409 line = br_.readLine();
410 else
411 line = null;
412
413 if (str_ != null)
414 {
415 while (line != null)
416 {
417 str_.println(line);
418 line = br_.readLine();
419 }
420 }
421 else
422 {
423 while (line != null)
424 {
425 line = br_.readLine();
426 }
427 }
428 }
429 catch (IOException _io)
430 {
431 System.err.println(_io.getMessage());
432 }
433
434 }
435 /**
436 * Ferme le flux avant d'interrompre le thread.
437 * @see java.lang.Thread#interrupt()
438 */
439 public void interrupt()
440 {
441 super.interrupt();
442 }
443
444 /**
445 * Ferme simplement le buffer. Cette methode doit avoir pour resultat
446 * d'arreter le thread par lui-meme.
447 */
448 private void closeBuffer()
449 {
450 try
451 {
452 if (br_ != null)
453 br_.close();
454 }
455 catch (IOException e)
456 {
457 }
458 }
459
460 /**
461 * @see java.lang.Thread#destroy()
462 */
463 public void destroy()
464 {
465 super.destroy();
466 }
467
468 }