View Javadoc

1   /***
2    * Cecilia ADL Compiler
3    * Copyright (C) 2006-2008 STMicroelectronics
4    *
5    * This library is free software; you can redistribute it and/or
6    * modify it under the terms of the GNU Lesser General Public
7    * License as published by the Free Software Foundation; either
8    * version 2 of the License, or (at your option) any later version.
9    *
10   * This library is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13   * Lesser General Public License for more details.
14   *
15   * You should have received a copy of the GNU Lesser General Public
16   * License along with this library; if not, write to the Free Software
17   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18   *
19   * Contact: fractal@objectweb.org
20   *
21   * Author:Matthieu Leclercq
22   */
23  
24  package org.objectweb.fractal.cecilia.adl.compiler;
25  
26  import java.io.BufferedReader;
27  import java.io.IOException;
28  import java.io.InputStreamReader;
29  import java.util.Arrays;
30  import java.util.List;
31  import java.util.logging.Level;
32  import java.util.logging.Logger;
33  
34  import org.objectweb.fractal.adl.ADLException;
35  import org.objectweb.fractal.adl.CompilerError;
36  import org.objectweb.fractal.adl.error.GenericErrors;
37  import org.objectweb.fractal.adl.util.FractalADLLogManager;
38  import org.objectweb.fractal.cecilia.adl.directives.DirectiveHelper;
39  
40  /**
41   * This helper class provides method to execute external commands.
42   */
43  public final class ExecutionHelper {
44    private ExecutionHelper() {
45    }
46  
47    // The io logger
48    protected static Logger logger = FractalADLLogManager.getLogger("io");
49  
50    /**
51     * Executes the given command line and returns the exit value. The given
52     * command is splited on space character boundary (this method is equivalent
53     * to <code>exec(execTitle, command.split("\\s"))</code>)<br>
54     * Note that the {@link #exec(String, List<String>)} method is safer since it
55     * while not split command line on space character boundary which may produce
56     * unexpected result if arguments may contains spaces.
57     * 
58     * @param command the command to execute.
59     * @return the exit value
60     * @throws ADLException If an error occurs while running the command.
61     * @throws InterruptedException if the calling thread has been interrupted
62     *             while waiting for the process to finish.
63     * @see #exec(String, List<String>)
64     */
65    public static int exec(final String command) throws ADLException,
66        InterruptedException {
67      return exec(null, command);
68    }
69  
70    /**
71     * Executes the given command line and returns the exit value. The given
72     * command is splited on space character boundary, unless the space is
73     * escaped by a backslash.<br>
74     * Note that the {@link #exec(String, List<String>)} method is safer since it
75     * while not split command line on space character boundary which may produce
76     * unexpected result if arguments may contains spaces.
77     * 
78     * @param execTitle the message to be logged as a header of the execution. May
79     *            be <code>null</code>.
80     * @param command the command to execute.
81     * @return the exit value
82     * @throws ADLException If an error occurs while running the command.
83     * @throws InterruptedException if the calling thread has been interrupted
84     *             while waiting for the process to finish.
85     * @see #exec(String, List<String>)
86     */
87    public static int exec(final String execTitle, final String command)
88        throws ADLException, InterruptedException {
89      return exec(execTitle, DirectiveHelper.splitOptionString(command));
90    }
91  
92    /**
93     * Executes the given command line and returns the exit value.<br>
94     * This method will issue some messages on the <code>io</code> logger. If
95     * the {@link Level#FINE FINE} level is enabled, the full command line will be
96     * logged. If the {@link Level#INFO INFO} level is enabled, the given
97     * <code>execTitle</code> will be logged. Finally, if the process produces
98     * output on its standard error stream. This output will be logged with the
99     * {@link Level#SEVERE SEVERE} level preceded by the <code>execTitle</code>
100    * if it has not been logged yet.
101    * 
102    * @param execTitle the message to be logged as a header of the execution. May
103    *            be <code>null</code>.
104    * @param cmdList the command to execute.
105    * @return the exit value
106    * @throws ADLException If an error occurs while running the command.
107    * @throws InterruptedException if the calling thread has been interrupted
108    *             while waiting for the process to finish.
109    */
110   public static int exec(final String execTitle, final List<String> cmdList)
111       throws ADLException, InterruptedException {
112     final Process process;
113     try {
114       process = new ProcessBuilder(cmdList).redirectErrorStream(true).start();
115     } catch (final IOException e1) {
116       throw new ADLException(CompilerErrors.EXECUTION_ERROR, cmdList.get(0));
117     }
118     final boolean titleLogged;
119 
120     if (logger.isLoggable(Level.INFO) && execTitle != null) {
121       logger.info(execTitle);
122       titleLogged = true;
123     } else {
124       titleLogged = false;
125     }
126 
127     if (logger.isLoggable(Level.FINE)) {
128       String command = "";
129       for (final String cmd : cmdList) {
130         command += cmd + " ";
131       }
132       logger.fine(command);
133     }
134 
135     // Read output produced by process in a parallele thread in order to avoid
136     // the process to block
137     final Thread readerThread = new Thread() {
138       @Override
139       public void run() {
140         // output output and error stream on the logger.
141         final BufferedReader reader = new BufferedReader(new InputStreamReader(
142             process.getInputStream()));
143         try {
144           String line = reader.readLine();
145           if (line != null) {
146             // if the title has not been printed yet.
147             if (!titleLogged) {
148               String command = "";
149               for (final String cmd : cmdList) {
150                 command += cmd + " ";
151               }
152               logger.severe((execTitle == null) ? command : execTitle);
153             }
154             do {
155               logger.severe(line);
156               line = reader.readLine();
157             } while (line != null);
158           }
159           reader.close();
160         } catch (final IOException e) {
161           throw new CompilerError(GenericErrors.INTERNAL_ERROR, e,
162               "Can't read error stream of process");
163         }
164       }
165     };
166 
167     readerThread.start();
168     final int rValue = process.waitFor();
169     readerThread.join();
170 
171     return rValue;
172   }
173 
174   /**
175    * Executes the given command line and returns the exit value.
176    *
177    * @param execTitle the message to be logged as a header of the execution. May
178    *            be <code>null</code>.
179    * @param cmdArray the command to execute.
180    * @return the exit value
181    * @throws ADLException If an error occurs while running the command.
182    * @throws InterruptedException if the calling thread has been interrupted
183    *             while waiting for the process to finish.
184    * @see #exec(String, List<String>)
185    * @see Runtime#exec(String[])
186    */
187   public static int exec(final String execTitle, final String[] cmdArray)
188       throws ADLException, InterruptedException {
189     return exec(execTitle, Arrays.asList(cmdArray));
190   }
191 }