KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > izforge > izpack > installer > ProcessPanelWorker


1 /*
2  * IzPack - Copyright 2001-2007 Julien Ponge, All Rights Reserved.
3  *
4  * http://www.izforge.com/izpack/
5  * http://developer.berlios.de/projects/izpack/
6  *
7  * Copyright 2004 Tino Schwarze
8  *
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  * http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  */

21
22 package com.izforge.izpack.installer;
23
24 import java.io.BufferedReader JavaDoc;
25 import java.io.File JavaDoc;
26 import java.io.FileOutputStream JavaDoc;
27 import java.io.IOException JavaDoc;
28 import java.io.InputStream JavaDoc;
29 import java.io.InputStreamReader JavaDoc;
30 import java.io.PrintWriter JavaDoc;
31 import java.lang.reflect.InvocationTargetException JavaDoc;
32 import java.lang.reflect.Method JavaDoc;
33 import java.text.SimpleDateFormat JavaDoc;
34 import java.util.ArrayList JavaDoc;
35 import java.util.Date JavaDoc;
36 import java.util.Iterator JavaDoc;
37 import java.util.List JavaDoc;
38 import java.util.Vector JavaDoc;
39
40 import net.n3.nanoxml.NonValidator;
41 import net.n3.nanoxml.StdXMLBuilder;
42 import net.n3.nanoxml.StdXMLParser;
43 import net.n3.nanoxml.StdXMLReader;
44 import net.n3.nanoxml.XMLElement;
45
46 import com.izforge.izpack.Pack;
47 import com.izforge.izpack.util.AbstractUIHandler;
48 import com.izforge.izpack.util.AbstractUIProcessHandler;
49 import com.izforge.izpack.util.Debug;
50 import com.izforge.izpack.util.IoHelper;
51 import com.izforge.izpack.util.OsConstraint;
52 import com.izforge.izpack.util.VariableSubstitutor;
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 JavaDoc
66 {
67
68     /** Name of resource for specifying processing parameters. */
69     private static final String JavaDoc SPEC_RESOURCE_NAME = "ProcessPanel.Spec.xml";
70
71     private VariableSubstitutor vs;
72
73     protected AbstractUIProcessHandler handler;
74
75     private ArrayList JavaDoc jobs = new ArrayList JavaDoc();
76
77     private boolean result = true;
78     
79     private static PrintWriter JavaDoc logfile = null;
80
81     private String JavaDoc logfiledir = null;
82
83     protected AutomatedInstallData idata;
84
85     /**
86      * The constructor.
87      *
88      * @param idata The installation data.
89      * @param handler The handler to notify of progress.
90      */

91     public ProcessPanelWorker(AutomatedInstallData idata, AbstractUIProcessHandler handler)
92             throws IOException JavaDoc
93     {
94         this.handler = handler;
95         this.idata = idata;
96         this.vs = new VariableSubstitutor(idata.getVariables());
97
98         // Removed this test in order to move out of the CTOR (ExecuteForPack
99
// Patch)
100
// if (!readSpec())
101
// throw new IOException("Error reading processing specification");
102
}
103
104     private boolean readSpec() throws IOException JavaDoc
105     {
106         InputStream JavaDoc input;
107         try
108         {
109             input = ResourceManager.getInstance().getInputStream(SPEC_RESOURCE_NAME);
110         }
111         catch (Exception JavaDoc 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         XMLElement spec;
122         try
123         {
124             parser.setReader(new StdXMLReader(input));
125
126             spec = (XMLElement) parser.parse();
127         }
128         catch (Exception JavaDoc e)
129         {
130             System.err.println("Error parsing XML specification for processing.");
131             System.err.println(e.toString());
132             return false;
133         }
134
135         if (!spec.hasChildren()) return false;
136
137         // Handle logfile
138
XMLElement lfd = spec.getFirstChildNamed("logfiledir");
139         if (lfd != null)
140         {
141             logfiledir = lfd.getContent();
142         }
143
144         for (Iterator JavaDoc job_it = spec.getChildrenNamed("job").iterator(); job_it.hasNext();)
145         {
146             XMLElement job_el = (XMLElement) job_it.next();
147
148             // ExecuteForPack Patch
149
// Check if processing required for pack
150
Vector JavaDoc forPacks = job_el.getChildrenNamed("executeForPack");
151             if (!jobRequiredFor(forPacks))
152             {
153                 continue;
154             }
155
156             // first check OS constraints - skip jobs not suited for this OS
157
List JavaDoc constraints = OsConstraint.getOsList(job_el);
158
159             if (OsConstraint.oneMatchesCurrentSystem(constraints))
160             {
161                 List JavaDoc ef_list = new ArrayList JavaDoc();
162
163                 String JavaDoc job_name = job_el.getAttribute("name", "");
164
165                 for (Iterator JavaDoc ef_it = job_el.getChildrenNamed("executefile").iterator(); ef_it
166                         .hasNext();)
167                 {
168                     XMLElement ef = (XMLElement) ef_it.next();
169
170                     String JavaDoc ef_name = ef.getAttribute("name");
171
172                     if ((ef_name == null) || (ef_name.length() == 0))
173                     {
174                         System.err.println("missing \"name\" attribute for <executefile>");
175                         return false;
176                     }
177
178                     List JavaDoc args = new ArrayList JavaDoc();
179
180                     for (Iterator JavaDoc arg_it = ef.getChildrenNamed("arg").iterator(); arg_it.hasNext();)
181                     {
182                         XMLElement arg_el = (XMLElement) arg_it.next();
183
184                         String JavaDoc arg_val = arg_el.getContent();
185
186                         args.add(arg_val);
187                     }
188
189                     ef_list.add(new ExecutableFile(ef_name, args));
190                 }
191
192                 for (Iterator JavaDoc ef_it = job_el.getChildrenNamed("executeclass").iterator(); ef_it
193                         .hasNext();)
194                 {
195                     XMLElement ef = (XMLElement) ef_it.next();
196                     String JavaDoc ef_name = ef.getAttribute("name");
197                     if ((ef_name == null) || (ef_name.length() == 0))
198                     {
199                         System.err.println("missing \"name\" attribute for <executeclass>");
200                         return false;
201                     }
202
203                     List JavaDoc args = new ArrayList JavaDoc();
204                     for (Iterator JavaDoc arg_it = ef.getChildrenNamed("arg").iterator(); arg_it.hasNext();)
205                     {
206                         XMLElement arg_el = (XMLElement) arg_it.next();
207                         String JavaDoc arg_val = arg_el.getContent();
208                         args.add(arg_val);
209                     }
210
211                     ef_list.add(new ExecutableClass(ef_name, args));
212                 }
213                 this.jobs.add(new ProcessingJob(job_name, ef_list));
214             }
215
216         }
217
218         return true;
219     }
220
221     /**
222      * This is called when the processing thread is activated.
223      *
224      * Can also be called directly if asynchronous processing is not desired.
225      */

226     public void run()
227     {
228         // ExecuteForPack patch
229
// Read spec only here... not before, cause packs are otherwise
230
// all selected or de-selected
231
try
232         {
233             if (!readSpec())
234             {
235                 System.err.println("Error parsing XML specification for processing.");
236                 return;
237             }
238         }
239         catch (java.io.IOException JavaDoc ioe)
240         {
241             System.err.println(ioe.toString());
242             return;
243         }
244
245         // Create logfile if needed. Do it at this point because
246
// variable substitution needs selected install path.
247
if (logfiledir != null)
248         {
249             logfiledir = IoHelper.translatePath(logfiledir, new VariableSubstitutor(idata
250                     .getVariables()));
251
252             File JavaDoc lf;
253
254             String JavaDoc appVersion = idata.getVariable("APP_VER");
255
256             if (appVersion != null)
257                 appVersion = "V" + appVersion;
258             else
259                 appVersion = "undef";
260
261             String JavaDoc identifier = (new SimpleDateFormat JavaDoc("yyyyMMddHHmmss")).format(new Date JavaDoc());
262
263             identifier = appVersion.replace(' ', '_') + "_" + identifier;
264
265             try
266             {
267                 lf = File.createTempFile("Install_" + identifier + "_", ".log",
268                         new File JavaDoc(logfiledir));
269                 logfile = new PrintWriter JavaDoc(new FileOutputStream JavaDoc(lf), true);
270             }
271             catch (IOException JavaDoc e)
272             {
273                 Debug.error(e);
274                 // TODO throw or throw not, that's the question...
275
}
276         }
277
278         this.handler.startProcessing(this.jobs.size());
279
280         for (Iterator JavaDoc job_it = this.jobs.iterator(); job_it.hasNext();)
281         {
282             ProcessingJob pj = (ProcessingJob) job_it.next();
283
284             this.handler.startProcess(pj.name);
285
286             this.result = pj.run(this.handler, this.vs);
287
288             this.handler.finishProcess();
289
290             if (!this.result) break;
291         }
292
293         this.handler.finishProcessing();
294         if (logfile != null) logfile.close();
295     }
296
297     /** Start the compilation in a separate thread. */
298     public void startThread()
299     {
300         Thread JavaDoc processingThread = new Thread JavaDoc(this, "processing thread");
301         // will call this.run()
302
processingThread.start();
303     }
304
305     /**
306      * Return the result of the process execution.
307      *
308      * @return true if all processes succeeded, false otherwise.
309      */

310     public boolean getResult()
311     {
312         return this.result;
313     }
314     
315     interface Processable
316     {
317
318         /**
319          * @param handler The UI handler for user interaction and to send output to.
320          * @return true on success, false if processing should stop
321          */

322         public boolean run(AbstractUIProcessHandler handler, VariableSubstitutor vs);
323     }
324
325     private static class ProcessingJob implements Processable
326     {
327
328         public String JavaDoc name;
329
330         private List JavaDoc processables;
331
332         public ProcessingJob(String JavaDoc name, List JavaDoc processables)
333         {
334             this.name = name;
335             this.processables = processables;
336         }
337
338         public boolean run(AbstractUIProcessHandler handler, VariableSubstitutor vs)
339         {
340             for (Iterator JavaDoc pr_it = this.processables.iterator(); pr_it.hasNext();)
341             {
342                 Processable pr = (Processable) pr_it.next();
343
344                 if (!pr.run(handler, vs)) return false;
345             }
346
347             return true;
348         }
349
350     }
351
352     private static class ExecutableFile implements Processable
353     {
354
355         private String JavaDoc filename;
356
357         private List JavaDoc arguments;
358
359         protected AbstractUIProcessHandler handler;
360
361         public ExecutableFile(String JavaDoc fn, List JavaDoc args)
362         {
363             this.filename = fn;
364             this.arguments = args;
365         }
366
367         public boolean run(AbstractUIProcessHandler handler, VariableSubstitutor vs)
368         {
369             this.handler = handler;
370
371             String JavaDoc params[] = new String JavaDoc[this.arguments.size() + 1];
372
373             params[0] = vs.substitute(this.filename, "plain");
374
375             int i = 1;
376             for (Iterator JavaDoc arg_it = this.arguments.iterator(); arg_it.hasNext();)
377             {
378                 params[i++] = vs.substitute((String JavaDoc) arg_it.next(), "plain");
379             }
380
381             try
382             {
383                 Process JavaDoc p = Runtime.getRuntime().exec(params);
384
385                 OutputMonitor stdoutMon = new OutputMonitor(this.handler, p.getInputStream(), false);
386                 OutputMonitor stderrMon = new OutputMonitor(this.handler, p.getErrorStream(), true);
387                 Thread JavaDoc stdoutThread = new Thread JavaDoc(stdoutMon);
388                 Thread JavaDoc stderrThread = new Thread JavaDoc(stderrMon);
389                 stdoutThread.setDaemon(true);
390                 stderrThread.setDaemon(true);
391                 stdoutThread.start();
392                 stderrThread.start();
393
394                 try
395                 {
396                     int exitStatus = p.waitFor();
397
398                     stopMonitor(stdoutMon, stdoutThread);
399                     stopMonitor(stderrMon, stderrThread);
400
401                     if (exitStatus != 0)
402                     {
403                         if (this.handler.askQuestion("process execution failed",
404                                 "Continue anyway?", AbstractUIHandler.CHOICES_YES_NO,
405                                 AbstractUIHandler.ANSWER_YES) == AbstractUIHandler.ANSWER_NO) { return false; }
406                     }
407                 }
408                 catch (InterruptedException JavaDoc ie)
409                 {
410                     p.destroy();
411                     this.handler.emitError("process interrupted", ie.toString());
412                     return false;
413                 }
414             }
415             catch (IOException JavaDoc ioe)
416             {
417                 this.handler.emitError("I/O error", ioe.toString());
418                 return false;
419             }
420
421             return true;
422         }
423
424         private void stopMonitor(OutputMonitor m, Thread JavaDoc t)
425         {
426             // taken from com.izforge.izpack.util.FileExecutor
427
m.doStop();
428             long softTimeout = 500;
429             try
430             {
431                 t.join(softTimeout);
432             }
433             catch (InterruptedException JavaDoc e)
434             {}
435
436             if (!t.isAlive()) return;
437
438             t.interrupt();
439             long hardTimeout = 500;
440             try
441             {
442                 t.join(hardTimeout);
443             }
444             catch (InterruptedException JavaDoc e)
445             {}
446         }
447
448         static public class OutputMonitor implements Runnable JavaDoc
449         {
450
451             private boolean stderr = false;
452
453             private AbstractUIProcessHandler handler;
454
455             private BufferedReader JavaDoc reader;
456
457             private Boolean JavaDoc stop = Boolean.valueOf(false);
458
459             public OutputMonitor(AbstractUIProcessHandler handler, InputStream JavaDoc is, boolean stderr)
460             {
461                 this.stderr = stderr;
462                 this.reader = new BufferedReader JavaDoc(new InputStreamReader JavaDoc(is));
463                 this.handler = handler;
464             }
465
466             public void run()
467             {
468                 try
469                 {
470                     String JavaDoc line;
471                     while ((line = reader.readLine()) != null)
472                     {
473                         this.handler.logOutput(line, stderr);
474
475                         // log output also to file given in ProcessPanelSpec
476

477                         if (logfile != null) logfile.println(line);
478
479                         synchronized (this.stop)
480                         {
481                             if (stop.booleanValue()) return;
482                         }
483                     }
484                 }
485                 catch (IOException JavaDoc ioe)
486                 {
487                     this.handler.logOutput(ioe.toString(), true);
488
489                     // log errors also to file given in ProcessPanelSpec
490

491                     if (logfile != null) logfile.println(ioe.toString());
492
493                 }
494
495             }
496
497             public void doStop()
498             {
499                 synchronized (this.stop)
500                 {
501                     this.stop = Boolean.valueOf(true);
502                 }
503             }
504
505         }
506
507     }
508
509     /**
510      * Tries to create a class that has an empty contstructor and a method
511      * run(AbstractUIProcessHandler, String[]) If found, it calls the method and processes all
512      * returned exceptions
513      */

514     private static class ExecutableClass implements Processable
515     {
516
517         final private String JavaDoc myClassName;
518
519         final private List JavaDoc myArguments;
520
521         protected AbstractUIProcessHandler myHandler;
522
523         public ExecutableClass(String JavaDoc className, List JavaDoc args)
524         {
525             myClassName = className;
526             myArguments = args;
527         }
528
529         public boolean run(AbstractUIProcessHandler aHandler, VariableSubstitutor varSubstitutor)
530         {
531             boolean result = false;
532             myHandler = aHandler;
533
534             String JavaDoc params[] = new String JavaDoc[myArguments.size()];
535
536             int i = 0;
537             for (Iterator JavaDoc arg_it = myArguments.iterator(); arg_it.hasNext();)
538                 params[i++] = varSubstitutor.substitute((String JavaDoc) arg_it.next(), "plain");
539
540             try
541             {
542                 ClassLoader JavaDoc loader = this.getClass().getClassLoader();
543                 Class JavaDoc procClass = loader.loadClass(myClassName);
544
545                 Object JavaDoc o = procClass.newInstance();
546                 Method JavaDoc m = procClass.getMethod("run", new Class JavaDoc[] { AbstractUIProcessHandler.class,
547                         String JavaDoc[].class});
548
549                 m.invoke(o, new Object JavaDoc[] { myHandler, params});
550                 result = true;
551             }
552             catch (SecurityException JavaDoc e)
553             {
554                 myHandler.emitError("Post Processing Error",
555                         "Security exception thrown when processing class: " + myClassName);
556             }
557             catch (ClassNotFoundException JavaDoc e)
558             {
559                 myHandler.emitError("Post Processing Error", "Cannot find processing class: "
560                         + myClassName);
561             }
562             catch (NoSuchMethodException JavaDoc e)
563             {
564                 myHandler.emitError("Post Processing Error",
565                         "Processing class does not have 'run' method: " + myClassName);
566             }
567             catch (IllegalAccessException JavaDoc e)
568             {
569                 myHandler.emitError("Post Processing Error", "Error accessing processing class: "
570                         + myClassName);
571             }
572             catch (InvocationTargetException JavaDoc e)
573             {
574                 myHandler.emitError("Post Processing Error", "Invocation Problem calling : "
575                         + myClassName + ", " + e.getCause().getMessage());
576             }
577             catch (Exception JavaDoc e)
578             {
579                 myHandler.emitError("Post Processing Error",
580                         "Exception when running processing class: " + myClassName + ", "
581                                 + e.getMessage());
582             }
583             catch (Error JavaDoc e)
584             {
585                 myHandler.emitError("Post Processing Error",
586                         "Error when running processing class: " + myClassName + ", "
587                                 + e.getMessage());
588             }
589             catch (Throwable JavaDoc e)
590             {
591                 myHandler.emitError("Post Processing Error",
592                         "Error when running processing class: " + myClassName + ", "
593                                 + e.getMessage());
594             }
595             return result;
596         }
597     }
598
599     /*------------------------ ExecuteForPack PATCH -------------------------*/
600     /*
601      * Verifies if the job is required for any of the packs listed. The job is required for a pack
602      * in the list if that pack is actually selected for installation. <br><br> <b>Note:</b><br>
603      * If the list of selected packs is empty then <code>true</code> is always returned. The same
604      * is true if the <code>packs</code> list is empty.
605      *
606      * @param packs a <code>Vector</code> of <code>String</code>s. Each of the strings denotes
607      * a pack for which the schortcut should be created if the pack is actually installed.
608      *
609      * @return <code>true</code> if the shortcut is required for at least on pack in the list,
610      * otherwise returns <code>false</code>.
611      */

612     /*--------------------------------------------------------------------------*/
613     /*
614      * @design
615      *
616      * The information about the installed packs comes from InstallData.selectedPacks. This assumes
617      * that this panel is presented to the user AFTER the PacksPanel.
618      *
619      * /*--------------------------------------------------------------------------
620      */

621
622     private boolean jobRequiredFor(Vector JavaDoc packs)
623     {
624         String JavaDoc selected;
625         String JavaDoc required;
626
627         if (packs.size() == 0) { return (true); }
628
629         // System.out.println ("Number of selected packs is "
630
// +idata.selectedPacks.size () );
631

632         for (int i = 0; i < idata.selectedPacks.size(); i++)
633         {
634             selected = ((Pack) idata.selectedPacks.get(i)).name;
635
636             // System.out.println ("Selected pack is " + selected);
637

638             for (int k = 0; k < packs.size(); k++)
639             {
640                 required = (String JavaDoc) ((XMLElement) packs.elementAt(k)).getAttribute("name", "");
641                 // System.out.println ("Attribute name is " + required);
642
if (selected.equals(required))
643                 {
644                     // System.out.println ("Return true");
645
return (true);
646                 }
647             }
648         }
649         return (false);
650     }
651
652 }
653
Popular Tags