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