View Javadoc
1   package org.apache.maven.plugin.surefire.report;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.BufferedOutputStream;
23  import java.io.File;
24  import java.io.FileOutputStream;
25  import java.io.FilterOutputStream;
26  import java.io.IOException;
27  import java.util.concurrent.atomic.AtomicStampedReference;
28  import java.util.concurrent.locks.ReentrantLock;
29  
30  import org.apache.maven.surefire.booter.DumpErrorSingleton;
31  import org.apache.maven.surefire.report.ReportEntry;
32  
33  import static org.apache.maven.plugin.surefire.report.FileReporter.getReportFile;
34  
35  /**
36   * Surefire output consumer proxy that writes test output to a {@link java.io.File} for each test suite.
37   *
38   * @author Kristian Rosenvold
39   * @author Carlos Sanchez
40   */
41  public class ConsoleOutputFileReporter
42      implements TestcycleConsoleOutputReceiver
43  {
44      private static final int STREAM_BUFFER_SIZE = 16 * 1024;
45      private static final int OPEN = 0;
46      private static final int CLOSED_TO_REOPEN = 1;
47      private static final int CLOSED = 2;
48  
49      private final File reportsDirectory;
50      private final String reportNameSuffix;
51  
52      private final AtomicStampedReference<FilterOutputStream> fileOutputStream =
53              new AtomicStampedReference<FilterOutputStream>( null, OPEN );
54  
55      private final ReentrantLock lock = new ReentrantLock();
56  
57      private volatile String reportEntryName;
58  
59      public ConsoleOutputFileReporter( File reportsDirectory, String reportNameSuffix )
60      {
61          this.reportsDirectory = reportsDirectory;
62          this.reportNameSuffix = reportNameSuffix;
63      }
64  
65      @Override
66      public void testSetStarting( ReportEntry reportEntry )
67      {
68          lock.lock();
69          try
70          {
71              closeNullReportFile( reportEntry );
72          }
73          finally
74          {
75              lock.unlock();
76          }
77      }
78  
79      @Override
80      public void testSetCompleted( ReportEntry report )
81      {
82      }
83  
84      @Override
85      public void close()
86      {
87          // The close() method is called in main Thread T2.
88          lock.lock();
89          try
90          {
91              closeReportFile();
92          }
93          finally
94          {
95              lock.unlock();
96          }
97      }
98  
99      @Override
100     public void writeTestOutput( byte[] buf, int off, int len, boolean stdout )
101     {
102         lock.lock();
103         try
104         {
105             // This method is called in single thread T1 per fork JVM (see ThreadedStreamConsumer).
106             // The close() method is called in main Thread T2.
107             int[] status = new int[1];
108             FilterOutputStream os = fileOutputStream.get( status );
109             if ( status[0] != CLOSED )
110             {
111                 if ( os == null )
112                 {
113                     if ( !reportsDirectory.exists() )
114                     {
115                         //noinspection ResultOfMethodCallIgnored
116                         reportsDirectory.mkdirs();
117                     }
118                     File file = getReportFile( reportsDirectory, reportEntryName, reportNameSuffix, "-output.txt" );
119                     os = new BufferedOutputStream( new FileOutputStream( file ), STREAM_BUFFER_SIZE );
120                     fileOutputStream.set( os, OPEN );
121                 }
122                 os.write( buf, off, len );
123             }
124         }
125         catch ( IOException e )
126         {
127             DumpErrorSingleton.getSingleton()
128                     .dumpException( e );
129 
130             throw new RuntimeException( e );
131         }
132         finally
133         {
134             lock.unlock();
135         }
136     }
137 
138     @SuppressWarnings( "checkstyle:emptyblock" )
139     private void closeNullReportFile( ReportEntry reportEntry )
140     {
141         try
142         {
143             // close null-output.txt report file
144             close( true );
145         }
146         catch ( IOException ignored )
147         {
148             DumpErrorSingleton.getSingleton()
149                     .dumpException( ignored );
150         }
151         finally
152         {
153             // prepare <class>-output.txt report file
154             reportEntryName = reportEntry.getName();
155         }
156     }
157 
158     @SuppressWarnings( "checkstyle:emptyblock" )
159     private void closeReportFile()
160     {
161         try
162         {
163             close( false );
164         }
165         catch ( IOException ignored )
166         {
167             DumpErrorSingleton.getSingleton()
168                     .dumpException( ignored );
169         }
170     }
171 
172     private void close( boolean closeReattempt )
173             throws IOException
174     {
175         int[] status = new int[1];
176         FilterOutputStream os = fileOutputStream.get( status );
177         if ( status[0] != CLOSED )
178         {
179             fileOutputStream.set( null, closeReattempt ? CLOSED_TO_REOPEN : CLOSED );
180             if ( os != null && status[0] == OPEN )
181             {
182                 os.close();
183             }
184         }
185     }
186 }