Statistics
| Revision:

root / import / ext3D / trunk / install-extension3d / IzPack / src / lib / com / izforge / izpack / util / FileExecutor.java @ 15280

History | View | Annotate | Download (10.7 KB)

1
/*
2
 *  $Id: FileExecutor.java,v 1.1 2006/06/14 07:29:07 cesar Exp $
3
 *  IzPack
4
 *  Copyright (C) 2002 Olexij Tkatchenko
5
 *
6
 *  File :               FileExecutor.java
7
 *  Description :        File execution class.
8
 *  Author's email :     ot@parcs.de
9
 *  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.util;
26

    
27
import java.io.BufferedReader;
28
import java.io.BufferedWriter;
29
import java.io.File;
30
import java.io.IOException;
31
import java.io.InputStreamReader;
32
import java.io.Reader;
33
import java.io.StringWriter;
34
import java.io.Writer;
35
import java.util.ArrayList;
36
import java.util.Collection;
37
import java.util.Iterator;
38
import java.util.List;
39

    
40
import com.izforge.izpack.ExecutableFile;
41

    
42
/**
43
 *  Executes a bunch of files. This class is intended to do a system dependent
44
 *  installation postprocessing. Executable file can be any file installed with
45
 *  current package. After execution the file can be optionally removed. Before
46
 *  execution on Unix systems execution flag will be set on processed file.
47
 *
48
 * @author     Olexij Tkatchenko <ot@parcs.de>
49
 */
50
public class FileExecutor
51
{
52
  /**
53
   *  This is a grabber for stdout and stderr. It will be launched once at
54
   *  command execution end terminates if the apropriate stream runs out of
55
   *  data.
56
   *
57
   * @author     Olexij Tkatchenko <ot@parcs.de>
58
   */
59
  private static class MonitorInputStream implements Runnable
60
  {
61

    
62
    private BufferedReader reader;
63
    private BufferedWriter writer;
64
    private boolean shouldStop = false;
65

    
66

    
67
    public MonitorInputStream(Reader in, Writer out)
68
    {
69
      reader = new BufferedReader(in);
70
      writer = new BufferedWriter(out);
71
    }
72

    
73

    
74
    public void doStop()
75
    {
76
      shouldStop = true;
77
    }
78

    
79

    
80
    public void run()
81
    {
82
      try
83
      {
84
        String line;
85
        while ((line = reader.readLine()) != null)
86
        {
87
          writer.write(line);
88
          writer.newLine();
89
          writer.flush();
90
          if (shouldStop)
91
            return;
92
        }
93
      }
94
      catch (IOException ioe)
95
      {
96
        ioe.printStackTrace(System.out);
97
      }
98
    }
99
  }
100

    
101

    
102
  private boolean stopThread(Thread t, MonitorInputStream m)
103
  {
104
    m.doStop();
105
    long softTimeout = 1000;
106
    try
107
    {
108
      t.join(softTimeout);
109
    }
110
    catch (InterruptedException e)
111
    {}
112

    
113
    if (!t.isAlive())
114
      return true;
115

    
116
    t.interrupt();
117
    long hardTimeout = 1000;
118
    try
119
    {
120
      t.join(hardTimeout);
121
    }
122
    catch (InterruptedException e)
123
    {}
124
    return !t.isAlive();
125
  }
126

    
127

    
128
  /**
129
   *  Constructs a new executor. The executable files specified must have
130
   *  pretranslated paths (variables expanded and file separator characters
131
   *  converted if necessary).
132
   *
133
   * @param  files  the executable files to process
134
   */
135
  public FileExecutor(Collection files)
136
  {
137
    this.files = files;
138
  }
139

    
140

    
141
  /**
142
   *  Constructs a new executor.
143
   */
144
  public FileExecutor ()
145
  {
146
    this.files = null;
147
  }
148

    
149
  /**
150
   *  Executed a system command and waits for completion.
151
   *
152
   * @param  params  system command as string array
153
   * @param  output  contains output of the command 
154
   *                 index 0 = standard output
155
   *                 index 1 = standard error
156
   * @return         exit status of process
157
   */
158
  public int executeCommand(String[] params, String[] output)
159
  {
160
    StringBuffer retval = new StringBuffer();
161
    retval.append("executeCommand\n");
162
    if (params != null)
163
    {
164
      for (int i = 0; i < params.length; i++)
165
      {
166
        retval.append("\tparams: "+params[i]);
167
        retval.append("\n");
168
      }
169
    }
170
    Process process = null;
171
    MonitorInputStream outMonitor = null;
172
    MonitorInputStream errMonitor = null;
173
    Thread t1 = null;
174
    Thread t2 = null;
175
    int exitStatus = -1;
176

    
177
    Debug.trace(retval);
178

    
179
    try
180
    {
181
      // execute command
182
      process = Runtime.getRuntime().exec(params);
183

    
184
      boolean console = false;//TODO: impl from xml <execute in_console=true ...>, but works already if this flag is true
185
      if (console)
186
      {
187
        Console c = new Console(process);
188
        // save command output
189
        output[0] = c.getOutputData();
190
        output[1] = c.getErrorData();
191
        exitStatus = process.exitValue();
192
      }
193
      else
194
      {
195
        StringWriter outWriter = new StringWriter();
196
        StringWriter errWriter = new StringWriter();
197

    
198
        InputStreamReader or =
199
          new InputStreamReader(process.getInputStream());
200
        InputStreamReader er =
201
          new InputStreamReader(process.getErrorStream());
202
        outMonitor = new MonitorInputStream(or, outWriter);
203
        errMonitor = new MonitorInputStream(er, errWriter);
204
        t1 = new Thread(outMonitor);
205
        t2 = new Thread(errMonitor);
206
        t1.setDaemon(true);
207
        t2.setDaemon(true);
208
        t1.start();
209
        t2.start();
210

    
211
        // wait for command to complete
212
        exitStatus = process.waitFor();
213
        t1.join();
214
        t2.join();
215

    
216
        // save command output
217
        output[0] = outWriter.toString();
218
        Debug.trace ("stdout:");
219
        Debug.trace (output[0]);
220
        output[1] = errWriter.toString();
221
        Debug.trace ("stderr:");
222
        Debug.trace (output[1]);
223
      }
224
      Debug.trace ("exit status: " + Integer.toString (exitStatus));
225
    }
226
    catch (InterruptedException e)
227
    {
228
      if (Debug.tracing()) e.printStackTrace(System.err);
229
      stopThread(t1, outMonitor);
230
      stopThread(t2, errMonitor);
231
      output[0] = "";
232
      output[1] = e.getMessage() + "\n";
233
      process.destroy();
234
    }
235
    catch (IOException e)
236
    {
237
      if (Debug.tracing()) e.printStackTrace(System.err);
238
      output[0] = "";
239
      output[1] = e.getMessage() + "\n";
240
    }
241
    return exitStatus;
242
  }
243

    
244
  /**
245
   *  Executes files specified at construction time.
246
   *
247
   * @param   currentStage the stage of the installation
248
   * @param   handler The AbstractUIHandler to notify on errors.
249
   * 
250
   * @return  0 on success, else the exit status of the last failed command
251
   */
252
  public int executeFiles(int currentStage, AbstractUIHandler handler)
253
  {
254
    int exitStatus = 0;
255
    String[] output = new String[2];
256
    //String permissions = (System.getProperty("user.name").equals("root")) ? "a+x" : "u+x";
257
    String permissions = "a+x";
258

    
259
    // loop through all executables
260
    Iterator efileIterator = files.iterator();
261
    while (exitStatus == 0 && efileIterator.hasNext())
262
    {
263
      ExecutableFile efile = (ExecutableFile) efileIterator.next();
264
      boolean deleteAfterwards = ! efile.keepFile;
265
      File file = new File(efile.path);
266
      Debug.trace("handeling executable file "+efile);
267

    
268
      // skip file if not for current OS (it might not have been installed at all)
269
      if (! OsConstraint.oneMatchesCurrentSystem (efile.osList))
270
        continue;
271
      
272
      if(currentStage!=ExecutableFile.UNINSTALL && OsVersion.IS_UNIX)
273
      {
274
        // fix executable permission for unix systems
275
        Debug.trace("making file executable (setting executable flag)");
276
        String[] params = {"/bin/chmod", permissions, file.toString()};
277
        exitStatus = executeCommand(params, output);
278
        if (exitStatus != 0)
279
        {
280
          handler.emitError("file execution error", "Error executing \n"+
281
            params[0]+" "+params[1]+" "+params[2]);
282
          continue;            
283
        }
284
      }
285

    
286
      // execute command in POSTINSTALL stage
287
      if ((exitStatus == 0) &&
288
          ((currentStage == ExecutableFile.POSTINSTALL && efile.executionStage == ExecutableFile.POSTINSTALL)
289
           || (currentStage==ExecutableFile.UNINSTALL && efile.executionStage == ExecutableFile.UNINSTALL)))
290
      {
291
        List paramList = new ArrayList();
292
        if (ExecutableFile.BIN == efile.type)
293
          paramList.add(file.toString());
294

    
295
        else if (ExecutableFile.JAR == efile.type && null == efile.mainClass)
296
        {
297
          paramList.add(System.getProperty("java.home") + "/bin/java");
298
          paramList.add("-jar");
299
          paramList.add(file.toString());
300
        }
301
        else if (ExecutableFile.JAR == efile.type && null != efile.mainClass)
302
        {
303
          paramList.add(System.getProperty("java.home") + "/bin/java");
304
          paramList.add("-cp");
305
          paramList.add(file.toString());
306
          paramList.add(efile.mainClass);
307
        }
308

    
309
        if (null != efile.argList && !efile.argList.isEmpty())
310
          paramList.addAll(efile.argList);
311

    
312
        String[] params = new String[paramList.size()];
313
        for (int i = 0; i < paramList.size(); i++)
314
          params[i] = (String) paramList.get(i);
315

    
316
        exitStatus = executeCommand(params, output);
317
          
318
        // bring a dialog depending on return code and failure handling
319
        if (exitStatus != 0)
320
        {
321
          deleteAfterwards = false;
322
          String message = output[0] + "\n" + output[1];
323
          if (message.length() == 1)
324
            message = "Failed to execute " + file.toString() + ".";
325

    
326
          if (efile.onFailure == ExecutableFile.ABORT)
327
          {
328
            // CHECKME: let the user decide or abort anyway?
329
            handler.emitError("file execution error", message);
330
          }
331
          else if (efile.onFailure == ExecutableFile.WARN)
332
          {
333
            // CHECKME: let the user decide or abort anyway?
334
            handler.emitWarning ("file execution error", message);
335
            exitStatus = 0;
336
          }
337
          else
338
          {
339
            if (handler.askQuestion (null, "Continue?", AbstractUIHandler.CHOICES_YES_NO) 
340
                == AbstractUIHandler.ANSWER_YES)
341
              exitStatus = 0;
342
          }
343

    
344
        }
345

    
346
      }
347
      
348

    
349
      // POSTINSTALL executables will be deleted
350
      if (efile.executionStage == ExecutableFile.POSTINSTALL && deleteAfterwards)
351
      {
352
        if (file.canWrite()) file.delete();
353
      }
354

    
355
    }
356
    return exitStatus;
357
  }
358

    
359
  /** The files to execute. */
360
  private Collection files;
361
}
362