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 }