Statistics
| Revision:

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
}