View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.shared.scriptinterpreter;
20  
21  import java.io.File;
22  import java.io.IOException;
23  import java.io.OutputStream;
24  import java.io.PrintStream;
25  import java.nio.file.Files;
26  
27  /**
28   * <p>FileLogger class.</p>
29   */
30  public class FileLogger implements ExecutionLogger, AutoCloseable {
31  
32      /**
33       * The path to the log file.
34       */
35      private File file;
36  
37      /**
38       * The underlying file stream this logger writes to.
39       */
40      private PrintStream stream;
41  
42      /**
43       * Creates a new logger that writes to the specified file.
44       *
45       * @param outputFile The path to the output file, if null all message will be discarded.
46       * @throws java.io.IOException If the output file could not be created.
47       */
48      public FileLogger(File outputFile) throws IOException {
49          this(outputFile, null);
50      }
51  
52      /**
53       * Creates a new logger that writes to the specified file and optionally mirrors messages.
54       *
55       * @param outputFile    The path to the output file, if null all message will be discarded.
56       * @param mirrorHandler The class which handle mirrored message, can be <code>null</code>.
57       * @throws java.io.IOException If the output file could not be created.
58       */
59      public FileLogger(File outputFile, FileLoggerMirrorHandler mirrorHandler) throws IOException {
60          this.file = outputFile;
61  
62          OutputStream outputStream;
63  
64          if (outputFile != null) {
65              outputFile.getParentFile().mkdirs();
66              outputStream = Files.newOutputStream(outputFile.toPath());
67          } else {
68              outputStream = new NullOutputStream();
69          }
70  
71          if (mirrorHandler != null) {
72              stream = new PrintStream(new MirrorStreamWrapper(outputStream, mirrorHandler));
73          } else {
74              stream = new PrintStream(outputStream);
75          }
76      }
77  
78      /**
79       * Gets the path to the output file.
80       *
81       * @return The path to the output file, never <code>null</code>.
82       */
83      public File getOutputFile() {
84          return file;
85      }
86  
87      /**
88       * Gets the underlying stream used to write message to the log file.
89       *
90       * @return The underlying stream used to write message to the log file, never <code>null</code>.
91       */
92      @Override
93      public PrintStream getPrintStream() {
94          return stream;
95      }
96  
97      /**
98       * Writes the specified line to the log file
99       * and invoke {@link FileLoggerMirrorHandler#consumeOutput(String)} if is given.
100      *
101      * @param line The message to log.
102      */
103     @Override
104     public void consumeLine(String line) {
105         stream.println(line);
106         stream.flush();
107     }
108 
109     /**
110      * Closes the underlying file stream.
111      */
112     public void close() {
113         if (stream != null) {
114             stream.flush();
115             stream.close();
116             stream = null;
117         }
118     }
119 
120     private static class MirrorStreamWrapper extends OutputStream {
121         private OutputStream out;
122 
123         private final FileLoggerMirrorHandler mirrorHandler;
124 
125         private StringBuilder lineBuffer;
126 
127         MirrorStreamWrapper(OutputStream outputStream, FileLoggerMirrorHandler mirrorHandler) {
128             this.out = outputStream;
129             this.mirrorHandler = mirrorHandler;
130             this.lineBuffer = new StringBuilder();
131         }
132 
133         @Override
134         public void write(int b) throws IOException {
135             out.write(b);
136             lineBuffer.append((char) (b));
137         }
138 
139         @Override
140         public void write(byte[] b, int off, int len) throws IOException {
141             out.write(b, off, len);
142             lineBuffer.append(new String(b, off, len));
143         }
144 
145         @Override
146         public void flush() throws IOException {
147             out.flush();
148 
149             int len = lineBuffer.length();
150             if (len == 0) {
151                 // nothing to log
152                 return;
153             }
154 
155             // remove line end for log
156             while (len > 0 && (lineBuffer.charAt(len - 1) == '\n' || lineBuffer.charAt(len - 1) == '\r')) {
157                 len--;
158             }
159             lineBuffer.setLength(len);
160 
161             mirrorHandler.consumeOutput(lineBuffer.toString());
162 
163             // clear buffer
164             lineBuffer = new StringBuilder();
165         }
166 
167         @Override
168         public void close() throws IOException {
169             flush();
170             if (out != null) {
171                 out.close();
172                 out = null;
173             }
174         }
175     }
176 
177     private static class NullOutputStream extends OutputStream {
178         @Override
179         public void write(int b) {
180             // do nothing
181         }
182 
183         @Override
184         public void write(byte[] b, int off, int len) {
185             // do nothing
186         }
187     }
188 }