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 2020/07/19 14:25:19 UTC

[maven-surefire] 01/01: [SUREFIRE-1827] The console output is not flushed

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

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

commit 27cc4cc75def55be1995ee559ea8c4ee16a5c9cf
Author: tibordigana <ti...@apache.org>
AuthorDate: Wed Jul 8 15:37:07 2020 +0200

    [SUREFIRE-1827] The console output is not flushed
---
 .../AbstractNoninterruptibleWritableChannel.java   | 15 ++--
 .../util/internal/WritableBufferedByteChannel.java |  1 +
 .../api/util/internal/ChannelsWriterTest.java      | 47 +++++++++--
 ...stractMasterProcessChannelProcessorFactory.java | 95 ++++++++++++++++++++++
 ...LegacyMasterProcessChannelProcessorFactory.java | 14 ++--
 ...refireMasterProcessChannelProcessorFactory.java | 10 ++-
 6 files changed, 156 insertions(+), 26 deletions(-)

diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/api/util/internal/AbstractNoninterruptibleWritableChannel.java b/surefire-api/src/main/java/org/apache/maven/surefire/api/util/internal/AbstractNoninterruptibleWritableChannel.java
index fe998f3..c2641e5 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/api/util/internal/AbstractNoninterruptibleWritableChannel.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/api/util/internal/AbstractNoninterruptibleWritableChannel.java
@@ -67,17 +67,12 @@ abstract class AbstractNoninterruptibleWritableChannel implements WritableBuffer
             src.flip();
         }
 
-        int countWrittenBytes = 0;
-
-        if ( src.hasRemaining() )
+        int countWrittenBytes = src.remaining();
+        writeImpl( src );
+        src.position( src.limit() );
+        if ( flush )
         {
-            countWrittenBytes = src.remaining();
-            writeImpl( src );
-            src.position( src.limit() );
-            if ( flush )
-            {
-                flushImpl();
-            }
+            flushImpl();
         }
         return countWrittenBytes;
     }
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/api/util/internal/WritableBufferedByteChannel.java b/surefire-api/src/main/java/org/apache/maven/surefire/api/util/internal/WritableBufferedByteChannel.java
index 42c0d08..973424a 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/api/util/internal/WritableBufferedByteChannel.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/api/util/internal/WritableBufferedByteChannel.java
@@ -29,6 +29,7 @@ import java.nio.channels.WritableByteChannel;
  * and the channel is flushed after the buffer has overflew.
  * <br>
  * The method {@link #write(ByteBuffer)} flushes every written message.
+ * You can flush the channel by {@link #write(ByteBuffer) writing} the zero length of {@link ByteBuffer}.
  */
 public interface WritableBufferedByteChannel extends WritableByteChannel
 {
diff --git a/surefire-api/src/test/java/org/apache/maven/surefire/api/util/internal/ChannelsWriterTest.java b/surefire-api/src/test/java/org/apache/maven/surefire/api/util/internal/ChannelsWriterTest.java
index 4befc24..24a09f3 100644
--- a/surefire-api/src/test/java/org/apache/maven/surefire/api/util/internal/ChannelsWriterTest.java
+++ b/surefire-api/src/test/java/org/apache/maven/surefire/api/util/internal/ChannelsWriterTest.java
@@ -137,6 +137,48 @@ public class ChannelsWriterTest
     }
 
     @Test
+    public void shouldFlushWhenEmptyBuffer() throws Exception
+    {
+        final boolean[] flushed = {false};
+        ByteArrayOutputStream out = new ByteArrayOutputStream()
+        {
+            @Override
+            public void flush() throws IOException
+            {
+                flushed[0] = true;
+                super.flush();
+            }
+        };
+        WritableByteChannel channel = Channels.newChannel( out );
+        ByteBuffer bb = ByteBuffer.allocate( 0 );
+        int countWritten = channel.write( bb );
+        assertThat( countWritten )
+            .isEqualTo( 0 );
+        assertThat( flushed[0] )
+            .isTrue();
+    }
+
+    @Test
+    public void shouldFlushWhenEmptyBufferOnBufferedWrites() throws Exception
+    {
+        final boolean[] flushed = {false};
+        ByteArrayOutputStream out = new ByteArrayOutputStream()
+        {
+            @Override
+            public void flush() throws IOException
+            {
+                flushed[0] = true;
+                super.flush();
+            }
+        };
+        WritableBufferedByteChannel channel = Channels.newBufferedChannel( out );
+        ByteBuffer bb = ByteBuffer.allocate( 0 );
+        channel.writeBuffered( bb );
+        assertThat( flushed[0] )
+            .isFalse();
+    }
+
+    @Test
     public void bufferedChannel() throws Exception
     {
         ByteArrayOutputStream out = new ByteArrayOutputStream();
@@ -151,11 +193,6 @@ public class ChannelsWriterTest
         assertThat( out.toByteArray() )
             .isEmpty();
 
-        channel.write( ByteBuffer.allocate( 0 ) );
-
-        assertThat( out.toByteArray() )
-            .isEmpty();
-
         channel.write( ByteBuffer.wrap( new byte[] {4} ) );
 
         assertThat( out.toByteArray() )
diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/AbstractMasterProcessChannelProcessorFactory.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/AbstractMasterProcessChannelProcessorFactory.java
new file mode 100644
index 0000000..99b2705
--- /dev/null
+++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/AbstractMasterProcessChannelProcessorFactory.java
@@ -0,0 +1,95 @@
+package org.apache.maven.surefire.booter.spi;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.surefire.spi.MasterProcessChannelProcessorFactory;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.WritableByteChannel;
+import java.security.AccessControlException;
+import java.security.PrivilegedAction;
+import java.util.concurrent.ScheduledExecutorService;
+
+import static java.security.AccessController.doPrivileged;
+import static java.util.concurrent.Executors.newScheduledThreadPool;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static org.apache.maven.surefire.api.util.internal.DaemonThreadFactory.newDaemonThreadFactory;
+
+/**
+ * Default implementation of {@link MasterProcessChannelProcessorFactory}.
+ */
+public abstract class AbstractMasterProcessChannelProcessorFactory
+    implements MasterProcessChannelProcessorFactory
+{
+    private static final String STREAM_FLUSHER = "surefire-forkedjvm-stream-flusher";
+    private final ScheduledExecutorService flusher;
+
+    public AbstractMasterProcessChannelProcessorFactory()
+    {
+        flusher = newScheduledThreadPool( 1, newDaemonThreadFactory( STREAM_FLUSHER ) );
+    }
+
+    protected void schedulePeriodicFlusher( int delayInMillis, final WritableByteChannel channel  )
+    {
+        flusher.scheduleWithFixedDelay( new Runnable()
+        {
+            @Override
+            public void run()
+            {
+                try
+                {
+                    channel.write( ByteBuffer.allocate( 0 ) );
+                }
+                catch ( Exception e )
+                {
+                    // cannot do anything about this I/O issue
+                }
+            }
+        }, 0L, delayInMillis, MILLISECONDS );
+    }
+
+    @Override
+    public void close() throws IOException
+    {
+        try
+        {
+            doPrivileged( new PrivilegedAction<Object>()
+                          {
+                              @Override
+                              public Object run()
+                              {
+                                  flusher.shutdown();
+                                  // Do NOT call awaitTermination() due to this would unnecessarily prolong teardown
+                                  // time of the JVM. It is not a critical situation when the JXM exits this daemon
+                                  // thread because the interrupted flusher does not break any business function.
+                                  // All business data is already safely flushed by test events, then by sending BYE
+                                  // event at the exit time and finally by flushEventChannelOnExit() in ForkedBooter.
+                                  return null;
+                              }
+                          }
+            );
+        }
+        catch ( AccessControlException e )
+        {
+            // ignore
+        }
+    }
+}
diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/LegacyMasterProcessChannelProcessorFactory.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/LegacyMasterProcessChannelProcessorFactory.java
index 1344f3d..bef079d 100644
--- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/LegacyMasterProcessChannelProcessorFactory.java
+++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/LegacyMasterProcessChannelProcessorFactory.java
@@ -21,7 +21,7 @@ package org.apache.maven.surefire.booter.spi;
 
 import org.apache.maven.surefire.api.booter.MasterProcessChannelDecoder;
 import org.apache.maven.surefire.api.booter.MasterProcessChannelEncoder;
-import org.apache.maven.surefire.spi.MasterProcessChannelProcessorFactory;
+import org.apache.maven.surefire.api.util.internal.WritableBufferedByteChannel;
 
 import java.io.IOException;
 import java.net.MalformedURLException;
@@ -36,7 +36,7 @@ import static org.apache.maven.surefire.api.util.internal.Channels.newBufferedCh
  * @since 3.0.0-M5
  */
 public class LegacyMasterProcessChannelProcessorFactory
-    implements MasterProcessChannelProcessorFactory
+    extends AbstractMasterProcessChannelProcessorFactory
 {
     @Override
     public boolean canUse( String channelConfig )
@@ -60,13 +60,11 @@ public class LegacyMasterProcessChannelProcessorFactory
     }
 
     @Override
+    @SuppressWarnings( "checkstyle:magicnumber" )
     public MasterProcessChannelEncoder createEncoder()
     {
-        return new LegacyMasterProcessChannelEncoder( newBufferedChannel( System.out ) );
-    }
-
-    @Override
-    public void close()
-    {
+        WritableBufferedByteChannel channel = newBufferedChannel( System.out );
+        schedulePeriodicFlusher( 200, channel );
+        return new LegacyMasterProcessChannelEncoder( channel );
     }
 }
diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/SurefireMasterProcessChannelProcessorFactory.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/SurefireMasterProcessChannelProcessorFactory.java
index 9efff25..9b4e6c1 100644
--- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/SurefireMasterProcessChannelProcessorFactory.java
+++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/SurefireMasterProcessChannelProcessorFactory.java
@@ -21,7 +21,7 @@ package org.apache.maven.surefire.booter.spi;
 
 import org.apache.maven.surefire.api.booter.MasterProcessChannelDecoder;
 import org.apache.maven.surefire.api.booter.MasterProcessChannelEncoder;
-import org.apache.maven.surefire.spi.MasterProcessChannelProcessorFactory;
+import org.apache.maven.surefire.api.util.internal.WritableBufferedByteChannel;
 
 import java.io.IOException;
 import java.net.InetSocketAddress;
@@ -53,7 +53,7 @@ import static org.apache.maven.surefire.api.util.internal.DaemonThreadFactory.ne
  * @since 3.0.0-M5
  */
 public class SurefireMasterProcessChannelProcessorFactory
-    implements MasterProcessChannelProcessorFactory
+    extends AbstractMasterProcessChannelProcessorFactory
 {
     private volatile AsynchronousSocketChannel clientSocketChannel;
 
@@ -102,14 +102,18 @@ public class SurefireMasterProcessChannelProcessorFactory
     }
 
     @Override
+    @SuppressWarnings( "checkstyle:magicnumber" )
     public MasterProcessChannelEncoder createEncoder()
     {
-        return new LegacyMasterProcessChannelEncoder( newBufferedChannel( newOutputStream( clientSocketChannel ) ) );
+        WritableBufferedByteChannel channel = newBufferedChannel( newOutputStream( clientSocketChannel ) );
+        schedulePeriodicFlusher( 200, channel );
+        return new LegacyMasterProcessChannelEncoder( channel );
     }
 
     @Override
     public void close() throws IOException
     {
+        super.close();
         if ( clientSocketChannel != null && clientSocketChannel.isOpen() )
         {
             clientSocketChannel.close();