1 | /*
|
---|
2 | * Copyright 2003-2005 The Apache Software Foundation
|
---|
3 | *
|
---|
4 | * Licensed under the Apache License, Version 2.0 (the "License");
|
---|
5 | * you may not use this file except in compliance with the License.
|
---|
6 | * You may obtain a copy of the License at
|
---|
7 | *
|
---|
8 | * http://www.apache.org/licenses/LICENSE-2.0
|
---|
9 | *
|
---|
10 | * Unless required by applicable law or agreed to in writing, software
|
---|
11 | * distributed under the License is distributed on an "AS IS" BASIS,
|
---|
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
---|
13 | * See the License for the specific language governing permissions and
|
---|
14 | * limitations under the License.
|
---|
15 | *
|
---|
16 | */
|
---|
17 | package org.apache.tools.ant.taskdefs;
|
---|
18 |
|
---|
19 | import java.io.File;
|
---|
20 | import java.io.Reader;
|
---|
21 | import java.io.InputStream;
|
---|
22 | import java.io.IOException;
|
---|
23 | import java.io.PrintStream;
|
---|
24 | import java.io.OutputStream;
|
---|
25 | import java.io.StringReader;
|
---|
26 | import java.io.BufferedReader;
|
---|
27 | import java.io.InputStreamReader;
|
---|
28 | import java.io.PipedOutputStream;
|
---|
29 | import java.io.ByteArrayInputStream;
|
---|
30 | import java.io.ByteArrayOutputStream;
|
---|
31 | import java.util.Arrays;
|
---|
32 | import java.util.Vector;
|
---|
33 |
|
---|
34 | import org.apache.tools.ant.Project;
|
---|
35 | import org.apache.tools.ant.ProjectComponent;
|
---|
36 | import org.apache.tools.ant.Task;
|
---|
37 | import org.apache.tools.ant.BuildException;
|
---|
38 | import org.apache.tools.ant.filters.util.ChainReaderHelper;
|
---|
39 | import org.apache.tools.ant.util.StringUtils;
|
---|
40 | import org.apache.tools.ant.util.TeeOutputStream;
|
---|
41 | import org.apache.tools.ant.util.ReaderInputStream;
|
---|
42 | import org.apache.tools.ant.util.LeadPipeInputStream;
|
---|
43 | import org.apache.tools.ant.util.LazyFileOutputStream;
|
---|
44 | import org.apache.tools.ant.util.OutputStreamFunneler;
|
---|
45 | import org.apache.tools.ant.util.ConcatFileInputStream;
|
---|
46 | import org.apache.tools.ant.util.KeepAliveOutputStream;
|
---|
47 |
|
---|
48 | /**
|
---|
49 | * The Redirector class manages the setup and connection of
|
---|
50 | * input and output redirection for an Ant project component.
|
---|
51 | *
|
---|
52 | * @since Ant 1.6
|
---|
53 | */
|
---|
54 | public class Redirector {
|
---|
55 |
|
---|
56 | private static final String DEFAULT_ENCODING
|
---|
57 | = System.getProperty("file.encoding");
|
---|
58 |
|
---|
59 | private class PropertyOutputStream extends ByteArrayOutputStream {
|
---|
60 | String property;
|
---|
61 | boolean closed = false;
|
---|
62 |
|
---|
63 | PropertyOutputStream(String property) {
|
---|
64 | super();
|
---|
65 | this.property = property;
|
---|
66 | }
|
---|
67 |
|
---|
68 | public void close() throws IOException {
|
---|
69 | if (!closed && !(append && appendProperties)) {
|
---|
70 | setPropertyFromBAOS(this, property);
|
---|
71 | closed = true;
|
---|
72 | }
|
---|
73 | }
|
---|
74 | }
|
---|
75 |
|
---|
76 | /**
|
---|
77 | * The file(s) from which standard input is being taken.
|
---|
78 | * If > 1, files' content will be concatenated in the order received.
|
---|
79 | */
|
---|
80 | private File[] input;
|
---|
81 |
|
---|
82 | /**
|
---|
83 | * The file(s) receiving standard output. Will also receive standard error
|
---|
84 | * unless standard error is redirected or logError is true.
|
---|
85 | */
|
---|
86 | private File[] out;
|
---|
87 |
|
---|
88 | /**
|
---|
89 | * The file(s) to which standard error is being redirected
|
---|
90 | */
|
---|
91 | private File[] error;
|
---|
92 |
|
---|
93 | /**
|
---|
94 | * Indicates if standard error should be logged to Ant's log system
|
---|
95 | * rather than the output. This has no effect if standard error is
|
---|
96 | * redirected to a file or property.
|
---|
97 | */
|
---|
98 | private boolean logError = false;
|
---|
99 |
|
---|
100 | /**
|
---|
101 | * Buffer used to capture output for storage into a property
|
---|
102 | */
|
---|
103 | private PropertyOutputStream baos = null;
|
---|
104 |
|
---|
105 | /**
|
---|
106 | * Buffer used to capture error output for storage into a property
|
---|
107 | */
|
---|
108 | private PropertyOutputStream errorBaos = null;
|
---|
109 |
|
---|
110 | /** The name of the property into which output is to be stored */
|
---|
111 | private String outputProperty;
|
---|
112 |
|
---|
113 | /** The name of the property into which error output is to be stored */
|
---|
114 | private String errorProperty;
|
---|
115 |
|
---|
116 | /** String from which input is taken */
|
---|
117 | private String inputString;
|
---|
118 |
|
---|
119 | /** Flag which indicates if error and output files are to be appended. */
|
---|
120 | private boolean append = false;
|
---|
121 |
|
---|
122 | /** Flag which indicates that output should be always sent to the log */
|
---|
123 | private boolean alwaysLog = false;
|
---|
124 |
|
---|
125 | /** Flag which indicates whether files should be created even when empty. */
|
---|
126 | private boolean createEmptyFiles = true;
|
---|
127 |
|
---|
128 | /** The task for which this redirector is working */
|
---|
129 | private ProjectComponent managingTask;
|
---|
130 |
|
---|
131 | /** The stream for output data */
|
---|
132 | private OutputStream outputStream = null;
|
---|
133 |
|
---|
134 | /** The stream for error output */
|
---|
135 | private OutputStream errorStream = null;
|
---|
136 |
|
---|
137 | /** The stream for input */
|
---|
138 | private InputStream inputStream = null;
|
---|
139 |
|
---|
140 | /** Stream which is used for line oriented output */
|
---|
141 | private PrintStream outPrintStream = null;
|
---|
142 |
|
---|
143 | /** Stream which is used for line oriented error output */
|
---|
144 | private PrintStream errorPrintStream = null;
|
---|
145 |
|
---|
146 | /** The output filter chains */
|
---|
147 | private Vector outputFilterChains;
|
---|
148 |
|
---|
149 | /** The error filter chains */
|
---|
150 | private Vector errorFilterChains;
|
---|
151 |
|
---|
152 | /** The input filter chains */
|
---|
153 | private Vector inputFilterChains;
|
---|
154 |
|
---|
155 | /** The output encoding */
|
---|
156 | private String outputEncoding = DEFAULT_ENCODING;
|
---|
157 |
|
---|
158 | /** The error encoding */
|
---|
159 | private String errorEncoding = DEFAULT_ENCODING;
|
---|
160 |
|
---|
161 | /** The input encoding */
|
---|
162 | private String inputEncoding = DEFAULT_ENCODING;
|
---|
163 |
|
---|
164 | /** Whether to complete properties settings **/
|
---|
165 | private boolean appendProperties = true;
|
---|
166 |
|
---|
167 | /** The thread group used for starting <code>StreamPumper</code> threads */
|
---|
168 | private ThreadGroup threadGroup = new ThreadGroup("redirector");
|
---|
169 |
|
---|
170 | /**
|
---|
171 | * Create a redirector instance for the given task
|
---|
172 | *
|
---|
173 | * @param managingTask the task for which the redirector is to work
|
---|
174 | */
|
---|
175 | public Redirector(Task managingTask) {
|
---|
176 | this((ProjectComponent) managingTask);
|
---|
177 | }
|
---|
178 |
|
---|
179 | /**
|
---|
180 | * Create a redirector instance for the given task
|
---|
181 | *
|
---|
182 | * @param managingTask the project component for which the
|
---|
183 | * redirector is to work
|
---|
184 | * @since Ant 1.6.3
|
---|
185 | */
|
---|
186 | public Redirector(ProjectComponent managingTask) {
|
---|
187 | this.managingTask = managingTask;
|
---|
188 | }
|
---|
189 |
|
---|
190 | /**
|
---|
191 | * Set the input to use for the task
|
---|
192 | *
|
---|
193 | * @param input the file from which input is read.
|
---|
194 | */
|
---|
195 | public void setInput(File input) {
|
---|
196 | setInput((input == null) ? null : new File[] {input});
|
---|
197 | }
|
---|
198 |
|
---|
199 | /**
|
---|
200 | * Set the input to use for the task
|
---|
201 | *
|
---|
202 | * @param input the files from which input is read.
|
---|
203 | */
|
---|
204 | public synchronized void setInput(File[] input) {
|
---|
205 | this.input = input;
|
---|
206 | }
|
---|
207 |
|
---|
208 | /**
|
---|
209 | * Set the string to use as input
|
---|
210 | *
|
---|
211 | * @param inputString the string which is used as the input source
|
---|
212 | */
|
---|
213 | public synchronized void setInputString(String inputString) {
|
---|
214 | this.inputString = inputString;
|
---|
215 | }
|
---|
216 |
|
---|
217 | /**
|
---|
218 | * Set a stream to use as input.
|
---|
219 | *
|
---|
220 | * @param inputStream the stream from which input will be read
|
---|
221 | * @since Ant 1.6.3
|
---|
222 | */
|
---|
223 | /*public*/ void setInputStream(InputStream inputStream) {
|
---|
224 | this.inputStream = inputStream;
|
---|
225 | }
|
---|
226 |
|
---|
227 | /**
|
---|
228 | * File the output of the process is redirected to. If error is not
|
---|
229 | * redirected, it too will appear in the output
|
---|
230 | *
|
---|
231 | * @param out the file to which output stream is written
|
---|
232 | */
|
---|
233 | public void setOutput(File out) {
|
---|
234 | setOutput((out == null) ? null : new File[] {out});
|
---|
235 | }
|
---|
236 |
|
---|
237 | /**
|
---|
238 | * Files the output of the process is redirected to. If error is not
|
---|
239 | * redirected, it too will appear in the output
|
---|
240 | *
|
---|
241 | * @param out the files to which output stream is written
|
---|
242 | */
|
---|
243 | public synchronized void setOutput(File[] out) {
|
---|
244 | this.out = out;
|
---|
245 | }
|
---|
246 |
|
---|
247 | /**
|
---|
248 | * Set the output encoding.
|
---|
249 | *
|
---|
250 | * @param outputEncoding <CODE>String</CODE>.
|
---|
251 | */
|
---|
252 | public synchronized void setOutputEncoding(String outputEncoding) {
|
---|
253 | if (outputEncoding == null) {
|
---|
254 | throw new IllegalArgumentException(
|
---|
255 | "outputEncoding must not be null");
|
---|
256 | } else {
|
---|
257 | this.outputEncoding = outputEncoding;
|
---|
258 | }
|
---|
259 | }
|
---|
260 |
|
---|
261 | /**
|
---|
262 | * Set the error encoding.
|
---|
263 | *
|
---|
264 | * @param errorEncoding <CODE>String</CODE>.
|
---|
265 | */
|
---|
266 | public synchronized void setErrorEncoding(String errorEncoding) {
|
---|
267 | if (errorEncoding == null) {
|
---|
268 | throw new IllegalArgumentException(
|
---|
269 | "errorEncoding must not be null");
|
---|
270 | } else {
|
---|
271 | this.errorEncoding = errorEncoding;
|
---|
272 | }
|
---|
273 | }
|
---|
274 |
|
---|
275 | /**
|
---|
276 | * Set the input encoding.
|
---|
277 | *
|
---|
278 | * @param inputEncoding <CODE>String</CODE>.
|
---|
279 | */
|
---|
280 | public synchronized void setInputEncoding(String inputEncoding) {
|
---|
281 | if (inputEncoding == null) {
|
---|
282 | throw new IllegalArgumentException(
|
---|
283 | "inputEncoding must not be null");
|
---|
284 | } else {
|
---|
285 | this.inputEncoding = inputEncoding;
|
---|
286 | }
|
---|
287 | }
|
---|
288 |
|
---|
289 | /**
|
---|
290 | * Controls whether error output of exec is logged. This is only useful
|
---|
291 | * when output is being redirected and error output is desired in the
|
---|
292 | * Ant log
|
---|
293 | *
|
---|
294 | * @param logError if true the standard error is sent to the Ant log system
|
---|
295 | * and not sent to output.
|
---|
296 | */
|
---|
297 | public synchronized void setLogError(boolean logError) {
|
---|
298 | this.logError = logError;
|
---|
299 | }
|
---|
300 |
|
---|
301 | /**
|
---|
302 | * This <CODE>Redirector</CODE>'s subordinate
|
---|
303 | * <CODE>PropertyOutputStream</CODE>s will not set their respective
|
---|
304 | * properties <CODE>while (appendProperties && append)</CODE>.
|
---|
305 | *
|
---|
306 | * @param appendProperties whether to append properties.
|
---|
307 | */
|
---|
308 | public synchronized void setAppendProperties(boolean appendProperties) {
|
---|
309 | this.appendProperties = appendProperties;
|
---|
310 | }
|
---|
311 |
|
---|
312 | /**
|
---|
313 | * Set the file to which standard error is to be redirected.
|
---|
314 | *
|
---|
315 | * @param error the file to which error is to be written
|
---|
316 | */
|
---|
317 | public void setError(File error) {
|
---|
318 | setError((error == null) ? null : new File[] {error});
|
---|
319 | }
|
---|
320 |
|
---|
321 | /**
|
---|
322 | * Set the files to which standard error is to be redirected.
|
---|
323 | *
|
---|
324 | * @param error the file to which error is to be written
|
---|
325 | */
|
---|
326 | public synchronized void setError(File[] error) {
|
---|
327 | this.error = error;
|
---|
328 | }
|
---|
329 |
|
---|
330 | /**
|
---|
331 | * Property name whose value should be set to the output of
|
---|
332 | * the process.
|
---|
333 | *
|
---|
334 | * @param outputProperty the name of the property to be set with the
|
---|
335 | * task's output.
|
---|
336 | */
|
---|
337 | public synchronized void setOutputProperty(String outputProperty) {
|
---|
338 | if (outputProperty == null
|
---|
339 | || !(outputProperty.equals(this.outputProperty))) {
|
---|
340 | this.outputProperty = outputProperty;
|
---|
341 | baos = null;
|
---|
342 | }
|
---|
343 | }
|
---|
344 |
|
---|
345 | /**
|
---|
346 | * Whether output should be appended to or overwrite an existing file.
|
---|
347 | * Defaults to false.
|
---|
348 | *
|
---|
349 | * @param append if true output and error streams are appended to their
|
---|
350 | * respective files, if specified.
|
---|
351 | */
|
---|
352 | public synchronized void setAppend(boolean append) {
|
---|
353 | this.append = append;
|
---|
354 | }
|
---|
355 |
|
---|
356 | /**
|
---|
357 | * If true, (error and non-error) output will be "teed", redirected
|
---|
358 | * as specified while being sent to Ant's logging mechanism as if no
|
---|
359 | * redirection had taken place. Defaults to false.
|
---|
360 | * @param alwaysLog <code>boolean</code>
|
---|
361 | * @since Ant 1.6.3
|
---|
362 | */
|
---|
363 | public synchronized void setAlwaysLog(boolean alwaysLog) {
|
---|
364 | this.alwaysLog = alwaysLog;
|
---|
365 | }
|
---|
366 |
|
---|
367 | /**
|
---|
368 | * Whether output and error files should be created even when empty.
|
---|
369 | * Defaults to true.
|
---|
370 | * @param createEmptyFiles <CODE>boolean</CODE>.
|
---|
371 | */
|
---|
372 | public synchronized void setCreateEmptyFiles(boolean createEmptyFiles) {
|
---|
373 | this.createEmptyFiles = createEmptyFiles;
|
---|
374 | }
|
---|
375 |
|
---|
376 | /**
|
---|
377 | * Property name whose value should be set to the error of
|
---|
378 | * the process.
|
---|
379 | *
|
---|
380 | * @param errorProperty the name of the property to be set
|
---|
381 | * with the error output.
|
---|
382 | */
|
---|
383 | public synchronized void setErrorProperty(String errorProperty) {
|
---|
384 | if (errorProperty == null
|
---|
385 | || !(errorProperty.equals(this.errorProperty))) {
|
---|
386 | this.errorProperty = errorProperty;
|
---|
387 | errorBaos = null;
|
---|
388 | }
|
---|
389 | }
|
---|
390 |
|
---|
391 | /**
|
---|
392 | * Set the input <CODE>FilterChain</CODE>s.
|
---|
393 | *
|
---|
394 | * @param inputFilterChains <CODE>Vector</CODE> containing <CODE>FilterChain</CODE>.
|
---|
395 | */
|
---|
396 | public synchronized void setInputFilterChains(Vector inputFilterChains) {
|
---|
397 | this.inputFilterChains = inputFilterChains;
|
---|
398 | }
|
---|
399 |
|
---|
400 | /**
|
---|
401 | * Set the output <CODE>FilterChain</CODE>s.
|
---|
402 | *
|
---|
403 | * @param outputFilterChains <CODE>Vector</CODE> containing <CODE>FilterChain</CODE>.
|
---|
404 | */
|
---|
405 | public synchronized void setOutputFilterChains(Vector outputFilterChains) {
|
---|
406 | this.outputFilterChains = outputFilterChains;
|
---|
407 | }
|
---|
408 |
|
---|
409 | /**
|
---|
410 | * Set the error <CODE>FilterChain</CODE>s.
|
---|
411 | *
|
---|
412 | * @param errorFilterChains <CODE>Vector</CODE> containing <CODE>FilterChain</CODE>.
|
---|
413 | */
|
---|
414 | public synchronized void setErrorFilterChains(Vector errorFilterChains) {
|
---|
415 | this.errorFilterChains = errorFilterChains;
|
---|
416 | }
|
---|
417 |
|
---|
418 | /**
|
---|
419 | * Set a property from a ByteArrayOutputStream
|
---|
420 | *
|
---|
421 | * @param baos contains the property value.
|
---|
422 | * @param propertyName the property name.
|
---|
423 | *
|
---|
424 | * @exception IOException if the value cannot be read form the stream.
|
---|
425 | */
|
---|
426 | private void setPropertyFromBAOS(ByteArrayOutputStream baos,
|
---|
427 | String propertyName) throws IOException {
|
---|
428 |
|
---|
429 | BufferedReader in
|
---|
430 | = new BufferedReader(new StringReader(Execute.toString(baos)));
|
---|
431 | String line = null;
|
---|
432 | StringBuffer val = new StringBuffer();
|
---|
433 | while ((line = in.readLine()) != null) {
|
---|
434 | if (val.length() != 0) {
|
---|
435 | val.append(StringUtils.LINE_SEP);
|
---|
436 | }
|
---|
437 | val.append(line);
|
---|
438 | }
|
---|
439 | managingTask.getProject().setNewProperty(propertyName, val.toString());
|
---|
440 | }
|
---|
441 |
|
---|
442 | /**
|
---|
443 | * Create the input, error and output streams based on the
|
---|
444 | * configuration options.
|
---|
445 | */
|
---|
446 | public synchronized void createStreams() {
|
---|
447 | if (out != null && out.length > 0) {
|
---|
448 | String logHead = new StringBuffer("Output ").append(
|
---|
449 | ((append) ? "appended" : "redirected")).append(
|
---|
450 | " to ").toString();
|
---|
451 | outputStream = foldFiles(out, logHead, Project.MSG_VERBOSE);
|
---|
452 | }
|
---|
453 | if (outputProperty != null) {
|
---|
454 | if (baos == null) {
|
---|
455 | baos = new PropertyOutputStream(outputProperty);
|
---|
456 | managingTask.log("Output redirected to property: "
|
---|
457 | + outputProperty, Project.MSG_VERBOSE);
|
---|
458 | }
|
---|
459 | //shield it from being closed by a filtering StreamPumper
|
---|
460 | OutputStream keepAliveOutput = new KeepAliveOutputStream(baos);
|
---|
461 | outputStream = (outputStream == null) ? keepAliveOutput
|
---|
462 | : new TeeOutputStream(outputStream, keepAliveOutput);
|
---|
463 | } else {
|
---|
464 | baos = null;
|
---|
465 | }
|
---|
466 |
|
---|
467 | if (error != null && error.length > 0) {
|
---|
468 | String logHead = new StringBuffer("Error ").append(
|
---|
469 | ((append) ? "appended" : "redirected")).append(
|
---|
470 | " to ").toString();
|
---|
471 | errorStream = foldFiles(error, logHead, Project.MSG_VERBOSE);
|
---|
472 | } else if (!(logError || outputStream == null)) {
|
---|
473 | long funnelTimeout = 0L;
|
---|
474 | OutputStreamFunneler funneler
|
---|
475 | = new OutputStreamFunneler(outputStream, funnelTimeout);
|
---|
476 | try {
|
---|
477 | outputStream = funneler.getFunnelInstance();
|
---|
478 | errorStream = funneler.getFunnelInstance();
|
---|
479 | } catch (IOException eyeOhEx) {
|
---|
480 | throw new BuildException(
|
---|
481 | "error splitting output/error streams", eyeOhEx);
|
---|
482 | }
|
---|
483 | }
|
---|
484 | if (errorProperty != null) {
|
---|
485 | if (errorBaos == null) {
|
---|
486 | errorBaos = new PropertyOutputStream(errorProperty);
|
---|
487 | managingTask.log("Error redirected to property: " + errorProperty,
|
---|
488 | Project.MSG_VERBOSE);
|
---|
489 | }
|
---|
490 | //shield it from being closed by a filtering StreamPumper
|
---|
491 | OutputStream keepAliveError = new KeepAliveOutputStream(errorBaos);
|
---|
492 | errorStream = (error == null || error.length == 0) ? keepAliveError
|
---|
493 | : new TeeOutputStream(errorStream, keepAliveError);
|
---|
494 | } else {
|
---|
495 | errorBaos = null;
|
---|
496 | }
|
---|
497 | if (alwaysLog || outputStream == null) {
|
---|
498 | OutputStream outputLog
|
---|
499 | = new LogOutputStream(managingTask, Project.MSG_INFO);
|
---|
500 | outputStream = (outputStream == null)
|
---|
501 | ? outputLog : new TeeOutputStream(outputLog, outputStream);
|
---|
502 | }
|
---|
503 | if (alwaysLog || errorStream == null) {
|
---|
504 | OutputStream errorLog
|
---|
505 | = new LogOutputStream(managingTask, Project.MSG_WARN);
|
---|
506 | errorStream = (errorStream == null)
|
---|
507 | ? errorLog : new TeeOutputStream(errorLog, errorStream);
|
---|
508 | }
|
---|
509 | if ((outputFilterChains != null && outputFilterChains.size() > 0)
|
---|
510 | || !(outputEncoding.equalsIgnoreCase(inputEncoding))) {
|
---|
511 | try {
|
---|
512 | LeadPipeInputStream snk = new LeadPipeInputStream();
|
---|
513 | snk.setManagingComponent(managingTask);
|
---|
514 |
|
---|
515 | InputStream outPumpIn = snk;
|
---|
516 |
|
---|
517 | Reader reader = new InputStreamReader(outPumpIn, inputEncoding);
|
---|
518 |
|
---|
519 | if (outputFilterChains != null && outputFilterChains.size() > 0) {
|
---|
520 | ChainReaderHelper helper = new ChainReaderHelper();
|
---|
521 | helper.setPrimaryReader(reader);
|
---|
522 | helper.setFilterChains(outputFilterChains);
|
---|
523 | reader = helper.getAssembledReader();
|
---|
524 | }
|
---|
525 | outPumpIn = new ReaderInputStream(reader, outputEncoding);
|
---|
526 |
|
---|
527 | Thread t = new Thread(threadGroup, new StreamPumper(
|
---|
528 | outPumpIn, outputStream, true), "output pumper");
|
---|
529 | t.setPriority(Thread.MAX_PRIORITY);
|
---|
530 | outputStream = new PipedOutputStream(snk);
|
---|
531 | t.start();
|
---|
532 | } catch (IOException eyeOhEx) {
|
---|
533 | throw new BuildException(
|
---|
534 | "error setting up output stream", eyeOhEx);
|
---|
535 | }
|
---|
536 | }
|
---|
537 |
|
---|
538 | if ((errorFilterChains != null && errorFilterChains.size() > 0)
|
---|
539 | || !(errorEncoding.equalsIgnoreCase(inputEncoding))) {
|
---|
540 | try {
|
---|
541 | LeadPipeInputStream snk = new LeadPipeInputStream();
|
---|
542 | snk.setManagingComponent(managingTask);
|
---|
543 |
|
---|
544 | InputStream errPumpIn = snk;
|
---|
545 |
|
---|
546 | Reader reader = new InputStreamReader(errPumpIn, inputEncoding);
|
---|
547 |
|
---|
548 | if (errorFilterChains != null && errorFilterChains.size() > 0) {
|
---|
549 | ChainReaderHelper helper = new ChainReaderHelper();
|
---|
550 | helper.setPrimaryReader(reader);
|
---|
551 | helper.setFilterChains(errorFilterChains);
|
---|
552 | reader = helper.getAssembledReader();
|
---|
553 | }
|
---|
554 | errPumpIn = new ReaderInputStream(reader, errorEncoding);
|
---|
555 |
|
---|
556 | Thread t = new Thread(threadGroup, new StreamPumper(
|
---|
557 | errPumpIn, errorStream, true), "error pumper");
|
---|
558 | t.setPriority(Thread.MAX_PRIORITY);
|
---|
559 | errorStream = new PipedOutputStream(snk);
|
---|
560 | t.start();
|
---|
561 | } catch (IOException eyeOhEx) {
|
---|
562 | throw new BuildException(
|
---|
563 | "error setting up error stream", eyeOhEx);
|
---|
564 | }
|
---|
565 | }
|
---|
566 |
|
---|
567 | // if input files are specified, inputString and inputStream are ignored;
|
---|
568 | // classes that work with redirector attributes can enforce
|
---|
569 | // whatever warnings are needed
|
---|
570 | if (input != null && input.length > 0) {
|
---|
571 | managingTask.log("Redirecting input from file"
|
---|
572 | + ((input.length == 1) ? "" : "s"), Project.MSG_VERBOSE);
|
---|
573 | try {
|
---|
574 | inputStream = new ConcatFileInputStream(input);
|
---|
575 | } catch (IOException eyeOhEx) {
|
---|
576 | throw new BuildException(eyeOhEx);
|
---|
577 | }
|
---|
578 | ((ConcatFileInputStream) inputStream).setManagingComponent(managingTask);
|
---|
579 | } else if (inputString != null) {
|
---|
580 | managingTask.log("Using input \"" + inputString + "\"",
|
---|
581 | Project.MSG_VERBOSE);
|
---|
582 | inputStream = new ByteArrayInputStream(inputString.getBytes());
|
---|
583 | }
|
---|
584 |
|
---|
585 | if (inputStream != null
|
---|
586 | && inputFilterChains != null && inputFilterChains.size() > 0) {
|
---|
587 | ChainReaderHelper helper = new ChainReaderHelper();
|
---|
588 | try {
|
---|
589 | helper.setPrimaryReader(
|
---|
590 | new InputStreamReader(inputStream, inputEncoding));
|
---|
591 | } catch (IOException eyeOhEx) {
|
---|
592 | throw new BuildException(
|
---|
593 | "error setting up input stream", eyeOhEx);
|
---|
594 | }
|
---|
595 | helper.setFilterChains(inputFilterChains);
|
---|
596 | inputStream = new ReaderInputStream(
|
---|
597 | helper.getAssembledReader(), inputEncoding);
|
---|
598 | }
|
---|
599 | }
|
---|
600 |
|
---|
601 | /**
|
---|
602 | * Create the StreamHandler to use with our Execute instance.
|
---|
603 | *
|
---|
604 | * @return the execute stream handler to manage the input, output and
|
---|
605 | * error streams.
|
---|
606 | *
|
---|
607 | * @throws BuildException if the execute stream handler cannot be created.
|
---|
608 | */
|
---|
609 | public synchronized ExecuteStreamHandler createHandler()
|
---|
610 | throws BuildException {
|
---|
611 | createStreams();
|
---|
612 | return new PumpStreamHandler(outputStream, errorStream, inputStream);
|
---|
613 | }
|
---|
614 |
|
---|
615 | /**
|
---|
616 | * Pass output sent to System.out to specified output.
|
---|
617 | *
|
---|
618 | * @param output the data to be output
|
---|
619 | */
|
---|
620 | protected synchronized void handleOutput(String output) {
|
---|
621 | if (outPrintStream == null) {
|
---|
622 | outPrintStream = new PrintStream(outputStream);
|
---|
623 | }
|
---|
624 | outPrintStream.print(output);
|
---|
625 | }
|
---|
626 |
|
---|
627 | /**
|
---|
628 | * Handle an input request
|
---|
629 | *
|
---|
630 | * @param buffer the buffer into which data is to be read.
|
---|
631 | * @param offset the offset into the buffer at which data is stored.
|
---|
632 | * @param length the amount of data to read
|
---|
633 | *
|
---|
634 | * @return the number of bytes read
|
---|
635 | *
|
---|
636 | * @exception IOException if the data cannot be read
|
---|
637 | */
|
---|
638 | protected synchronized int handleInput(byte[] buffer, int offset,
|
---|
639 | int length) throws IOException {
|
---|
640 | if (inputStream == null) {
|
---|
641 | return managingTask.getProject().defaultInput(buffer, offset,
|
---|
642 | length);
|
---|
643 | } else {
|
---|
644 | return inputStream.read(buffer, offset, length);
|
---|
645 | }
|
---|
646 | }
|
---|
647 |
|
---|
648 | /**
|
---|
649 | * Process data due to a flush operation.
|
---|
650 | *
|
---|
651 | * @param output the data being flushed.
|
---|
652 | */
|
---|
653 | protected synchronized void handleFlush(String output) {
|
---|
654 | if (outPrintStream == null) {
|
---|
655 | outPrintStream = new PrintStream(outputStream);
|
---|
656 | }
|
---|
657 | outPrintStream.print(output);
|
---|
658 | outPrintStream.flush();
|
---|
659 | }
|
---|
660 |
|
---|
661 | /**
|
---|
662 | * Process error output
|
---|
663 | *
|
---|
664 | * @param output the error output data.
|
---|
665 | */
|
---|
666 | protected synchronized void handleErrorOutput(String output) {
|
---|
667 | if (errorPrintStream == null) {
|
---|
668 | errorPrintStream = new PrintStream(errorStream);
|
---|
669 | }
|
---|
670 | errorPrintStream.print(output);
|
---|
671 | }
|
---|
672 |
|
---|
673 | /**
|
---|
674 | * Handle a flush operation on the error stream
|
---|
675 | *
|
---|
676 | * @param output the error information being flushed.
|
---|
677 | */
|
---|
678 | protected synchronized void handleErrorFlush(String output) {
|
---|
679 | if (errorPrintStream == null) {
|
---|
680 | errorPrintStream = new PrintStream(errorStream);
|
---|
681 | }
|
---|
682 | errorPrintStream.print(output);
|
---|
683 | }
|
---|
684 |
|
---|
685 | /**
|
---|
686 | * Get the output stream for the redirector
|
---|
687 | *
|
---|
688 | * @return the redirector's output stream or null if no output
|
---|
689 | * has been configured
|
---|
690 | */
|
---|
691 | public synchronized OutputStream getOutputStream() {
|
---|
692 | return outputStream;
|
---|
693 | }
|
---|
694 |
|
---|
695 | /**
|
---|
696 | * Get the error stream for the redirector
|
---|
697 | *
|
---|
698 | * @return the redirector's error stream or null if no output
|
---|
699 | * has been configured
|
---|
700 | */
|
---|
701 | public synchronized OutputStream getErrorStream() {
|
---|
702 | return errorStream;
|
---|
703 | }
|
---|
704 |
|
---|
705 | /**
|
---|
706 | * Get the input stream for the redirector
|
---|
707 | *
|
---|
708 | * @return the redirector's input stream or null if no output
|
---|
709 | * has been configured
|
---|
710 | */
|
---|
711 | public synchronized InputStream getInputStream() {
|
---|
712 | return inputStream;
|
---|
713 | }
|
---|
714 |
|
---|
715 | /**
|
---|
716 | * Complete redirection.
|
---|
717 | *
|
---|
718 | * This operation will close any streams and create any specified
|
---|
719 | * property values.
|
---|
720 | *
|
---|
721 | * @throws IOException if the output properties cannot be read from their
|
---|
722 | * output streams.
|
---|
723 | */
|
---|
724 | public synchronized void complete() throws IOException {
|
---|
725 | System.out.flush();
|
---|
726 | System.err.flush();
|
---|
727 |
|
---|
728 | if (inputStream != null) {
|
---|
729 | inputStream.close();
|
---|
730 | }
|
---|
731 |
|
---|
732 | outputStream.flush();
|
---|
733 | outputStream.close();
|
---|
734 |
|
---|
735 | errorStream.flush();
|
---|
736 | errorStream.close();
|
---|
737 |
|
---|
738 | //wait for the StreamPumpers to finish
|
---|
739 | while (threadGroup.activeCount() > 0) {
|
---|
740 | try {
|
---|
741 | managingTask.log("waiting for " + threadGroup.activeCount()
|
---|
742 | + " Threads:", Project.MSG_DEBUG);
|
---|
743 | Thread[] thread = new Thread[threadGroup.activeCount()];
|
---|
744 | threadGroup.enumerate(thread);
|
---|
745 | for (int i = 0; i < thread.length && thread[i] != null; i++) {
|
---|
746 | try {
|
---|
747 | managingTask.log(thread[i].toString(), Project.MSG_DEBUG);
|
---|
748 | } catch (NullPointerException enPeaEx) {
|
---|
749 | // Ignore exception
|
---|
750 | }
|
---|
751 | }
|
---|
752 | Thread.sleep(1000);
|
---|
753 | } catch (InterruptedException eyeEx) {
|
---|
754 | // Ignore exception
|
---|
755 | }
|
---|
756 | }
|
---|
757 |
|
---|
758 | setProperties();
|
---|
759 |
|
---|
760 | inputStream = null;
|
---|
761 | outputStream = null;
|
---|
762 | errorStream = null;
|
---|
763 | outPrintStream = null;
|
---|
764 | errorPrintStream = null;
|
---|
765 | }
|
---|
766 |
|
---|
767 | /**
|
---|
768 | * Notify the <CODE>Redirector</CODE> that it is now okay
|
---|
769 | * to set any output and/or error properties.
|
---|
770 | */
|
---|
771 | public synchronized void setProperties() {
|
---|
772 | if (baos != null) {
|
---|
773 | try {
|
---|
774 | baos.close();
|
---|
775 | } catch (IOException eyeOhEx) {
|
---|
776 | // Ignore exception
|
---|
777 | }
|
---|
778 | }
|
---|
779 | if (errorBaos != null) {
|
---|
780 | try {
|
---|
781 | errorBaos.close();
|
---|
782 | } catch (IOException eyeOhEx) {
|
---|
783 | // Ignore exception
|
---|
784 | }
|
---|
785 | }
|
---|
786 | }
|
---|
787 |
|
---|
788 | private OutputStream foldFiles(File[] file, String logHead, int loglevel) {
|
---|
789 | OutputStream result
|
---|
790 | = new LazyFileOutputStream(file[0], append, createEmptyFiles);
|
---|
791 |
|
---|
792 | managingTask.log(logHead + file[0], loglevel);
|
---|
793 | char[] c = new char[logHead.length()];
|
---|
794 | Arrays.fill(c, ' ');
|
---|
795 | String indent = new String(c);
|
---|
796 |
|
---|
797 | for (int i = 1; i < file.length; i++) {
|
---|
798 | outputStream = new TeeOutputStream(outputStream,
|
---|
799 | new LazyFileOutputStream(file[i], append, createEmptyFiles));
|
---|
800 | managingTask.log(indent + file[i], loglevel);
|
---|
801 | }
|
---|
802 | return result;
|
---|
803 | }
|
---|
804 | }
|
---|