You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by ti...@apache.org on 2018/03/25 20:59:37 UTC

[maven-surefire] branch 1506 updated: [SUREFIRE-1506] Sporadic NullPointerException in ConsoleOutputFileReporter#close()

This is an automated email from the ASF dual-hosted git repository.

tibordigana pushed a commit to branch 1506
in repository https://gitbox.apache.org/repos/asf/maven-surefire.git


The following commit(s) were added to refs/heads/1506 by this push:
     new 7c678dc  [SUREFIRE-1506] Sporadic NullPointerException in ConsoleOutputFileReporter#close()
7c678dc is described below

commit 7c678dc5ee61c917132e6cabb03e1cf875742c01
Author: Tibor17 <ti...@apache.org>
AuthorDate: Sun Mar 25 22:59:17 2018 +0200

    [SUREFIRE-1506] Sporadic NullPointerException in ConsoleOutputFileReporter#close()
---
 .../surefire/report/ConsoleOutputFileReporter.java | 87 ++++++++++++++--------
 1 file changed, 55 insertions(+), 32 deletions(-)

diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/ConsoleOutputFileReporter.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/ConsoleOutputFileReporter.java
index 84cbabe..b5e4c3d 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/ConsoleOutputFileReporter.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/ConsoleOutputFileReporter.java
@@ -22,8 +22,10 @@ package org.apache.maven.plugin.surefire.report;
 import java.io.BufferedOutputStream;
 import java.io.File;
 import java.io.FileOutputStream;
+import java.io.FilterOutputStream;
 import java.io.IOException;
-import java.io.OutputStream;
+import java.util.concurrent.atomic.AtomicStampedReference;
+import java.util.concurrent.locks.ReentrantLock;
 
 import org.apache.maven.surefire.report.ReportEntry;
 
@@ -44,9 +46,12 @@ public class ConsoleOutputFileReporter
 
     private final String reportNameSuffix;
 
-    private String reportEntryName;
+    private final AtomicStampedReference<FilterOutputStream> fileOutputStream =
+            new AtomicStampedReference<FilterOutputStream>( null, 0 );
+
+    private final ReentrantLock lock = new ReentrantLock();
 
-    private OutputStream fileOutputStream;
+    private String reportEntryName;
 
     public ConsoleOutputFileReporter( File reportsDirectory, String reportNameSuffix )
     {
@@ -57,7 +62,7 @@ public class ConsoleOutputFileReporter
     @Override
     public void testSetStarting( ReportEntry reportEntry )
     {
-        close();
+        close( true );
         reportEntryName = reportEntry.getName();
     }
 
@@ -67,53 +72,71 @@ public class ConsoleOutputFileReporter
     }
 
     @Override
-    @SuppressWarnings( "checkstyle:emptyblock" )
     public void close()
     {
-        if ( fileOutputStream != null )
+        // The close() method is called in main Thread T2.
+        close( false );
+    }
+
+    @Override
+    public void writeTestOutput( byte[] buf, int off, int len, boolean stdout )
+    {
+        lock.lock();
+        try
         {
-            //noinspection EmptyCatchBlock
-            try
-            {
-                fileOutputStream.flush();
-            }
-            catch ( IOException e )
+            // This method is called in single thread T1 per fork JVM (see ThreadedStreamConsumer).
+            // The close() method is called in main Thread T2.
+            int[] stamp = {0};
+            FilterOutputStream os = fileOutputStream.get( stamp );
+            if ( stamp[0] != 2 )
             {
-            }
-            finally
-            {
-                try
-                {
-                    fileOutputStream.close();
-                }
-                catch ( IOException ignored )
+                if ( os == null )
                 {
+                    if ( !reportsDirectory.exists() )
+                    {
+                        //noinspection ResultOfMethodCallIgnored
+                        reportsDirectory.mkdirs();
+                    }
+                    File file = getReportFile( reportsDirectory, reportEntryName, reportNameSuffix, "-output.txt" );
+                    os = new BufferedOutputStream( new FileOutputStream( file ), 16 * 1024 );
+                    fileOutputStream.set( os, 0 );
                 }
+                os.write( buf, off, len );
             }
-            fileOutputStream = null;
+        }
+        catch ( IOException e )
+        {
+            throw new RuntimeException( e );
+        }
+        finally
+        {
+            lock.unlock();
         }
     }
 
-    @Override
-    public void writeTestOutput( byte[] buf, int off, int len, boolean stdout )
+    @SuppressWarnings( "checkstyle:emptyblock" )
+    private void close( boolean closeReattempt )
     {
+        lock.lock();
         try
         {
-            if ( fileOutputStream == null )
+            int[] stamp = {0};
+            FilterOutputStream os = fileOutputStream.get( stamp );
+            if ( stamp[0] != 2 )
             {
-                if ( !reportsDirectory.exists() )
+                fileOutputStream.set( null, closeReattempt ? 1 : 2 );
+                if ( os != null && stamp[0] == 0 )
                 {
-                    //noinspection ResultOfMethodCallIgnored
-                    reportsDirectory.mkdirs();
+                    os.close();
                 }
-                File file = getReportFile( reportsDirectory, reportEntryName, reportNameSuffix, "-output.txt" );
-                fileOutputStream = new BufferedOutputStream( new FileOutputStream( file ), 16 * 1024 );
             }
-            fileOutputStream.write( buf, off, len );
         }
-        catch ( IOException e )
+        catch ( IOException ignored )
         {
-            throw new RuntimeException( e );
+        }
+        finally
+        {
+            lock.unlock();
         }
     }
 }

-- 
To stop receiving notification emails like this one, please contact
tibordigana@apache.org.