root / import / ext3D / trunk / install-extension3d / IzPack / src / lib / com / izforge / izpack / installer / ProcessPanelWorker.java @ 15280
History | View | Annotate | Download (15.9 KB)
1 | 15280 | rgaitan | /*
|
---|---|---|---|
2 | * $Id: ProcessPanelWorker.java,v 1.1 2006/06/14 07:29:07 cesar Exp $
|
||
3 | * IzPack
|
||
4 | * Copyright (C) 2001-2004 Julien Ponge, Tino Schwarze
|
||
5 | *
|
||
6 | * File : CompilePanel.java
|
||
7 | * Description : A panel to compile files after installation
|
||
8 | * Author's email : julien@izforge.com
|
||
9 | * Author's Website : http://www.izforge.com
|
||
10 | *
|
||
11 | * This program is free software; you can redistribute it and/or
|
||
12 | * modify it under the terms of the GNU General Public License
|
||
13 | * as published by the Free Software Foundation; either version 2
|
||
14 | * of the License, or any later version.
|
||
15 | *
|
||
16 | * This program is distributed in the hope that it will be useful,
|
||
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
19 | * GNU General Public License for more details.
|
||
20 | *
|
||
21 | * You should have received a copy of the GNU General Public License
|
||
22 | * along with this program; if not, write to the Free Software
|
||
23 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||
24 | */
|
||
25 | package com.izforge.izpack.installer; |
||
26 | |||
27 | import java.io.BufferedReader; |
||
28 | import java.io.File; |
||
29 | import java.io.FileOutputStream; |
||
30 | import java.io.IOException; |
||
31 | import java.io.InputStream; |
||
32 | import java.io.InputStreamReader; |
||
33 | import java.io.PrintWriter; |
||
34 | import java.lang.reflect.InvocationTargetException; |
||
35 | import java.lang.reflect.Method; |
||
36 | import java.text.SimpleDateFormat; |
||
37 | import java.util.ArrayList; |
||
38 | import java.util.Date; |
||
39 | import java.util.Iterator; |
||
40 | import java.util.List; |
||
41 | |||
42 | import net.n3.nanoxml.NonValidator; |
||
43 | import net.n3.nanoxml.StdXMLBuilder; |
||
44 | import net.n3.nanoxml.StdXMLParser; |
||
45 | import net.n3.nanoxml.StdXMLReader; |
||
46 | import net.n3.nanoxml.XMLElement; |
||
47 | |||
48 | import com.izforge.izpack.util.AbstractUIHandler; |
||
49 | import com.izforge.izpack.util.AbstractUIProcessHandler; |
||
50 | import com.izforge.izpack.util.Debug; |
||
51 | import com.izforge.izpack.util.IoHelper; |
||
52 | import com.izforge.izpack.util.OsConstraint; |
||
53 | |||
54 | /**
|
||
55 | * This class does alle the work for the process panel.
|
||
56 | *
|
||
57 | * It responsible for
|
||
58 | * <ul>
|
||
59 | * <li>parsing the process spec XML file
|
||
60 | * <li>performing the actions described therein
|
||
61 | * </ul>
|
||
62 | *
|
||
63 | * @author Tino Schwarze
|
||
64 | */
|
||
65 | public class ProcessPanelWorker implements Runnable |
||
66 | { |
||
67 | /** Name of resource for specifying processing parameters. */
|
||
68 | private static final String SPEC_RESOURCE_NAME = "ProcessPanel.Spec.xml"; |
||
69 | |||
70 | private VariableSubstitutor vs;
|
||
71 | |||
72 | private XMLElement spec;
|
||
73 | |||
74 | protected AbstractUIProcessHandler handler;
|
||
75 | |||
76 | private ArrayList jobs = new ArrayList(); |
||
77 | |||
78 | private Thread processingThread = null; |
||
79 | |||
80 | private static PrintWriter logfile = null; |
||
81 | |||
82 | private String logfiledir = null; |
||
83 | |||
84 | protected AutomatedInstallData idata;
|
||
85 | |||
86 | /**
|
||
87 | * The constructor.
|
||
88 | *
|
||
89 | * @param idata The installation data.
|
||
90 | * @param handler The handler to notify of progress.
|
||
91 | */
|
||
92 | public ProcessPanelWorker(
|
||
93 | AutomatedInstallData idata, |
||
94 | AbstractUIProcessHandler handler) |
||
95 | throws IOException |
||
96 | { |
||
97 | this.handler = handler;
|
||
98 | this.idata = idata;
|
||
99 | this.vs = new VariableSubstitutor(idata.getVariables()); |
||
100 | |||
101 | if (!readSpec())
|
||
102 | throw new IOException("Error reading processing specification"); |
||
103 | } |
||
104 | |||
105 | private boolean readSpec() throws IOException |
||
106 | { |
||
107 | InputStream input;
|
||
108 | try
|
||
109 | { |
||
110 | input = ResourceManager.getInstance().getInputStream(SPEC_RESOURCE_NAME); |
||
111 | } catch (Exception e) |
||
112 | { |
||
113 | e.printStackTrace(); |
||
114 | return false; |
||
115 | } |
||
116 | |||
117 | StdXMLParser parser = new StdXMLParser();
|
||
118 | parser.setBuilder(new StdXMLBuilder());
|
||
119 | parser.setValidator(new NonValidator());
|
||
120 | |||
121 | try
|
||
122 | { |
||
123 | parser.setReader(new StdXMLReader(input));
|
||
124 | |||
125 | this.spec = (XMLElement) parser.parse();
|
||
126 | } catch (Exception e) |
||
127 | { |
||
128 | System.err.println("Error parsing XML specification for processing."); |
||
129 | System.err.println(e.toString());
|
||
130 | return false; |
||
131 | } |
||
132 | |||
133 | if (!this.spec.hasChildren()) |
||
134 | return false; |
||
135 | |||
136 | // Handle logfile
|
||
137 | XMLElement lfd = spec.getFirstChildNamed("logfiledir");
|
||
138 | if( lfd != null) |
||
139 | { |
||
140 | logfiledir = lfd.getContent(); |
||
141 | } |
||
142 | |||
143 | |||
144 | for (Iterator job_it = this.spec.getChildrenNamed("job").iterator(); |
||
145 | job_it.hasNext(); |
||
146 | ) |
||
147 | { |
||
148 | XMLElement job_el = (XMLElement) job_it.next(); |
||
149 | |||
150 | // first check OS constraints - skip jobs not suited for this OS
|
||
151 | List constraints = OsConstraint.getOsList(job_el);
|
||
152 | |||
153 | if (OsConstraint.oneMatchesCurrentSystem(constraints))
|
||
154 | { |
||
155 | List ef_list = new ArrayList(); |
||
156 | |||
157 | String job_name = job_el.getAttribute("name", ""); |
||
158 | |||
159 | for (Iterator ef_it = job_el.getChildrenNamed("executefile").iterator(); |
||
160 | ef_it.hasNext(); |
||
161 | ) |
||
162 | { |
||
163 | XMLElement ef = (XMLElement) ef_it.next(); |
||
164 | |||
165 | String ef_name = ef.getAttribute("name"); |
||
166 | |||
167 | if ((ef_name == null) || (ef_name.length() == 0)) |
||
168 | { |
||
169 | System.err.println("missing \"name\" attribute for <executefile>"); |
||
170 | return false; |
||
171 | } |
||
172 | |||
173 | List args = new ArrayList(); |
||
174 | |||
175 | for (Iterator arg_it = ef.getChildrenNamed("arg").iterator(); |
||
176 | arg_it.hasNext(); |
||
177 | ) |
||
178 | { |
||
179 | XMLElement arg_el = (XMLElement) arg_it.next(); |
||
180 | |||
181 | String arg_val = arg_el.getContent();
|
||
182 | |||
183 | args.add(arg_val); |
||
184 | } |
||
185 | |||
186 | ef_list.add(new ExecutableFile(ef_name, args));
|
||
187 | } |
||
188 | |||
189 | for (Iterator ef_it = job_el.getChildrenNamed("executeclass").iterator(); ef_it.hasNext();) |
||
190 | { |
||
191 | XMLElement ef = (XMLElement) ef_it.next(); |
||
192 | String ef_name = ef.getAttribute("name"); |
||
193 | if ((ef_name == null) || (ef_name.length() == 0)) |
||
194 | { |
||
195 | System.err.println("missing \"name\" attribute for <executeclass>"); |
||
196 | return false; |
||
197 | } |
||
198 | |||
199 | List args = new ArrayList(); |
||
200 | for (Iterator arg_it = ef.getChildrenNamed("arg").iterator(); arg_it.hasNext();) |
||
201 | { |
||
202 | XMLElement arg_el = (XMLElement) arg_it.next(); |
||
203 | String arg_val = arg_el.getContent();
|
||
204 | args.add(arg_val); |
||
205 | } |
||
206 | |||
207 | ef_list.add(new ExecutableClass(ef_name, args));
|
||
208 | } |
||
209 | this.jobs.add(new ProcessingJob(job_name, ef_list)); |
||
210 | } |
||
211 | |||
212 | } |
||
213 | |||
214 | return true; |
||
215 | } |
||
216 | |||
217 | /** This is called when the processing thread is activated.
|
||
218 | *
|
||
219 | * Can also be called directly if asynchronous processing is not
|
||
220 | * desired.
|
||
221 | */
|
||
222 | public void run() |
||
223 | { |
||
224 | // Create logfile if needed. Do it at this point because
|
||
225 | // variable substitution needs selected install path.
|
||
226 | if( logfiledir != null ) |
||
227 | { |
||
228 | logfiledir = IoHelper.translatePath(logfiledir,new VariableSubstitutor(idata.getVariables()));
|
||
229 | |||
230 | File lf;
|
||
231 | |||
232 | String appVersion = idata.getVariable("APP_VER"); |
||
233 | |||
234 | if (appVersion != null) |
||
235 | appVersion = "V"+appVersion;
|
||
236 | else
|
||
237 | appVersion = "undef";
|
||
238 | |||
239 | String identifier = (new SimpleDateFormat("yyyyMMddHHmmss")).format(new Date()); |
||
240 | |||
241 | identifier = appVersion.replace(' ', '_') + "_" + identifier; |
||
242 | |||
243 | try
|
||
244 | { |
||
245 | lf = File.createTempFile("Install_"+identifier+"_", ".log", new File(logfiledir)); |
||
246 | logfile = new PrintWriter(new FileOutputStream( lf ), true); |
||
247 | } |
||
248 | catch (IOException e) |
||
249 | { |
||
250 | Debug.error(e); |
||
251 | // TODO throw or throw not, that's the question...
|
||
252 | } |
||
253 | } |
||
254 | |||
255 | this.handler.startProcessing(this.jobs.size()); |
||
256 | |||
257 | for (Iterator job_it = this.jobs.iterator(); job_it.hasNext();) |
||
258 | { |
||
259 | ProcessingJob pj = (ProcessingJob) job_it.next(); |
||
260 | |||
261 | this.handler.startProcess(pj.name);
|
||
262 | |||
263 | boolean result = pj.run(this.handler, this.vs); |
||
264 | |||
265 | this.handler.finishProcess();
|
||
266 | |||
267 | if (!result)
|
||
268 | break;
|
||
269 | } |
||
270 | |||
271 | this.handler.finishProcessing();
|
||
272 | if( logfile != null ) |
||
273 | logfile.close(); |
||
274 | } |
||
275 | |||
276 | /** Start the compilation in a separate thread. */
|
||
277 | public void startThread() |
||
278 | { |
||
279 | this.processingThread = new Thread(this, "processing thread"); |
||
280 | //will call this.run()
|
||
281 | this.processingThread.start();
|
||
282 | } |
||
283 | |||
284 | interface Processable |
||
285 | { |
||
286 | /**
|
||
287 | * @param handler The UI handler for user interaction and to send output to.
|
||
288 | * @return true on success, false if processing should stop
|
||
289 | */
|
||
290 | public boolean run( |
||
291 | AbstractUIProcessHandler handler, |
||
292 | VariableSubstitutor vs); |
||
293 | } |
||
294 | |||
295 | private static class ProcessingJob implements Processable |
||
296 | { |
||
297 | public String name; |
||
298 | private List processables; |
||
299 | |||
300 | public ProcessingJob(String name, List processables) |
||
301 | { |
||
302 | this.name = name;
|
||
303 | this.processables = processables;
|
||
304 | } |
||
305 | |||
306 | public boolean run( |
||
307 | AbstractUIProcessHandler handler, |
||
308 | VariableSubstitutor vs) |
||
309 | { |
||
310 | for (Iterator pr_it = this.processables.iterator(); pr_it.hasNext();) |
||
311 | { |
||
312 | Processable pr = (Processable) pr_it.next(); |
||
313 | |||
314 | if (!pr.run(handler, vs))
|
||
315 | return false; |
||
316 | } |
||
317 | |||
318 | return true; |
||
319 | } |
||
320 | |||
321 | } |
||
322 | |||
323 | private static class ExecutableFile implements Processable |
||
324 | { |
||
325 | private String filename; |
||
326 | private List arguments; |
||
327 | protected AbstractUIProcessHandler handler;
|
||
328 | |||
329 | public ExecutableFile(String fn, List args) |
||
330 | { |
||
331 | this.filename = fn;
|
||
332 | this.arguments = args;
|
||
333 | } |
||
334 | |||
335 | public boolean run( |
||
336 | AbstractUIProcessHandler handler, |
||
337 | VariableSubstitutor vs) |
||
338 | { |
||
339 | this.handler = handler;
|
||
340 | |||
341 | String params[] = new String[this.arguments.size() + 1]; |
||
342 | |||
343 | params[0] = vs.substitute(this.filename, "plain"); |
||
344 | |||
345 | int i = 1; |
||
346 | for (Iterator arg_it = this.arguments.iterator(); arg_it.hasNext();) |
||
347 | { |
||
348 | params[i++] = vs.substitute((String) arg_it.next(), "plain"); |
||
349 | } |
||
350 | |||
351 | try
|
||
352 | { |
||
353 | Process p = Runtime.getRuntime().exec(params); |
||
354 | |||
355 | OutputMonitor stdoutMon = |
||
356 | new OutputMonitor(this.handler, p.getInputStream(), false); |
||
357 | OutputMonitor stderrMon = |
||
358 | new OutputMonitor(this.handler, p.getErrorStream(), true); |
||
359 | Thread stdoutThread = new Thread(stdoutMon); |
||
360 | Thread stderrThread = new Thread(stderrMon); |
||
361 | stdoutThread.setDaemon(true);
|
||
362 | stderrThread.setDaemon(true);
|
||
363 | stdoutThread.start(); |
||
364 | stderrThread.start(); |
||
365 | |||
366 | try
|
||
367 | { |
||
368 | int exitStatus = p.waitFor();
|
||
369 | |||
370 | stopMonitor(stdoutMon, stdoutThread); |
||
371 | stopMonitor(stderrMon, stderrThread); |
||
372 | |||
373 | if (exitStatus != 0) |
||
374 | { |
||
375 | if (this |
||
376 | .handler |
||
377 | .askQuestion( |
||
378 | "process execution failed",
|
||
379 | "Continue anyway?",
|
||
380 | AbstractUIHandler.CHOICES_YES_NO, |
||
381 | AbstractUIHandler.ANSWER_YES) |
||
382 | == AbstractUIHandler.ANSWER_NO) |
||
383 | { |
||
384 | return false; |
||
385 | } |
||
386 | } |
||
387 | } catch (InterruptedException ie) |
||
388 | { |
||
389 | p.destroy(); |
||
390 | this.handler.emitError("process interrupted", ie.toString()); |
||
391 | return false; |
||
392 | } |
||
393 | } catch (IOException ioe) |
||
394 | { |
||
395 | this.handler.emitError("I/O error", ioe.toString()); |
||
396 | return false; |
||
397 | } |
||
398 | |||
399 | return true; |
||
400 | } |
||
401 | |||
402 | private void stopMonitor(OutputMonitor m, Thread t) |
||
403 | { |
||
404 | // taken from com.izforge.izpack.util.FileExecutor
|
||
405 | m.doStop(); |
||
406 | long softTimeout = 500; |
||
407 | try
|
||
408 | { |
||
409 | t.join(softTimeout); |
||
410 | } catch (InterruptedException e) |
||
411 | { |
||
412 | } |
||
413 | |||
414 | if (t.isAlive() == false) |
||
415 | return;
|
||
416 | |||
417 | t.interrupt(); |
||
418 | long hardTimeout = 500; |
||
419 | try
|
||
420 | { |
||
421 | t.join(hardTimeout); |
||
422 | } catch (InterruptedException e) |
||
423 | { |
||
424 | } |
||
425 | } |
||
426 | |||
427 | static public class OutputMonitor implements Runnable |
||
428 | { |
||
429 | private boolean stderr = false; |
||
430 | private AbstractUIProcessHandler handler;
|
||
431 | private BufferedReader reader; |
||
432 | private Boolean stop = Boolean.valueOf(false); |
||
433 | |||
434 | public OutputMonitor(
|
||
435 | AbstractUIProcessHandler handler, |
||
436 | InputStream is,
|
||
437 | boolean stderr)
|
||
438 | { |
||
439 | this.stderr = stderr;
|
||
440 | this.reader = new BufferedReader(new InputStreamReader(is)); |
||
441 | this.handler = handler;
|
||
442 | } |
||
443 | |||
444 | public void run() |
||
445 | { |
||
446 | try
|
||
447 | { |
||
448 | String line;
|
||
449 | while ((line = reader.readLine()) != null) |
||
450 | { |
||
451 | this.handler.logOutput(line, stderr);
|
||
452 | |||
453 | // log output also to file given in ProcessPanelSpec
|
||
454 | |||
455 | if( logfile != null ) |
||
456 | logfile.println( line); |
||
457 | |||
458 | synchronized (this.stop) |
||
459 | { |
||
460 | if (stop.booleanValue())
|
||
461 | return;
|
||
462 | } |
||
463 | } |
||
464 | } catch (IOException ioe) |
||
465 | { |
||
466 | this.handler.logOutput(ioe.toString(), true); |
||
467 | |||
468 | // log errors also to file given in ProcessPanelSpec
|
||
469 | |||
470 | if( logfile != null ) |
||
471 | logfile.println( ioe.toString()); |
||
472 | |||
473 | } |
||
474 | |||
475 | } |
||
476 | |||
477 | public void doStop() |
||
478 | { |
||
479 | synchronized (this.stop) |
||
480 | { |
||
481 | this.stop = Boolean.valueOf(true); |
||
482 | } |
||
483 | } |
||
484 | |||
485 | } |
||
486 | |||
487 | } |
||
488 | |||
489 | |||
490 | /**
|
||
491 | * Tries to create a class that has an empty contstructor and
|
||
492 | * a method run(AbstractUIProcessHandler, String[])
|
||
493 | * If found, it calls the method and processes all returned exceptions
|
||
494 | */
|
||
495 | private static class ExecutableClass implements Processable |
||
496 | { |
||
497 | final private String myClassName; |
||
498 | final private List myArguments; |
||
499 | protected AbstractUIProcessHandler myHandler;
|
||
500 | |||
501 | public ExecutableClass(String className, List args) |
||
502 | { |
||
503 | myClassName = className; |
||
504 | myArguments = args; |
||
505 | } |
||
506 | |||
507 | public boolean run( AbstractUIProcessHandler aHandler, VariableSubstitutor varSubstitutor) |
||
508 | { |
||
509 | boolean result = false; |
||
510 | myHandler = aHandler; |
||
511 | |||
512 | String params[] = new String[myArguments.size()]; |
||
513 | |||
514 | int i = 0; |
||
515 | for (Iterator arg_it = myArguments.iterator(); arg_it.hasNext();) |
||
516 | params[i++] = varSubstitutor.substitute((String) arg_it.next(), "plain"); |
||
517 | |||
518 | try {
|
||
519 | ClassLoader loader = this.getClass().getClassLoader(); |
||
520 | Class procClass = loader.loadClass(myClassName);
|
||
521 | |||
522 | Object o = procClass.newInstance();
|
||
523 | Method m = procClass.getMethod("run", new Class[] {AbstractUIProcessHandler.class, String[].class}); |
||
524 | |||
525 | m.invoke(o, new Object[] {myHandler, params}); |
||
526 | result = true;
|
||
527 | } |
||
528 | catch (SecurityException e) { |
||
529 | myHandler.emitError("Post Processing Error", "Security exception thrown when processing class: " + myClassName); |
||
530 | } |
||
531 | catch (ClassNotFoundException e) { |
||
532 | myHandler.emitError("Post Processing Error", "Cannot find processing class: " + myClassName); |
||
533 | } |
||
534 | catch (NoSuchMethodException e) { |
||
535 | myHandler.emitError("Post Processing Error", "Processing class does not have 'run' method: " + myClassName); |
||
536 | } |
||
537 | catch (IllegalAccessException e) { |
||
538 | myHandler.emitError("Post Processing Error", "Error accessing processing class: " + myClassName); |
||
539 | } |
||
540 | catch (InvocationTargetException e) { |
||
541 | myHandler.emitError("Post Processing Error", "Invocation Problem calling : " + myClassName + ", " + e.getCause().getMessage()); |
||
542 | } |
||
543 | catch (Exception e) { |
||
544 | myHandler.emitError("Post Processing Error", "Exception when running processing class: " + myClassName + ", " + e.getMessage()); |
||
545 | } |
||
546 | catch( Error e) |
||
547 | { |
||
548 | myHandler.emitError("Post Processing Error", "Error when running processing class: " + myClassName + ", " + e.getMessage()); |
||
549 | } |
||
550 | catch( Throwable e) |
||
551 | { |
||
552 | myHandler.emitError("Post Processing Error", "Error when running processing class: " + myClassName + ", " + e.getMessage()); |
||
553 | } |
||
554 | return result;
|
||
555 | } |
||
556 | } |
||
557 | |||
558 | |||
559 | |||
560 | } |