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

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

commit 7be86d1ee9c7992dc7a48a218ac4318c320c2b1b
Author: Romain Manni-Bucau <rmannibu...@gmail.com>
AuthorDate: Wed Jul 8 13:02:23 2020 +0200

    enable to flush regularly output of the forked process
---
 .../plugin/surefire/AbstractSurefireMojo.java      | 15 +++++++-
 .../surefire/booterclient/BooterSerializer.java    |  5 +++
 ...ooterDeserializerProviderConfigurationTest.java |  2 +-
 ...BooterDeserializerStartupConfigurationTest.java |  2 +-
 .../AbstractNoninterruptibleWritableChannel.java   |  6 +++
 .../util/internal/WritableBufferedByteChannel.java |  2 +
 .../maven/surefire/booter/BooterConstants.java     |  1 +
 .../maven/surefire/booter/BooterDeserializer.java  |  4 +-
 .../apache/maven/surefire/booter/ForkedBooter.java | 45 ++++++++++++++++++++++
 .../surefire/booter/ProviderConfiguration.java     | 10 ++++-
 .../spi/LegacyMasterProcessChannelEncoder.java     |  5 +++
 11 files changed, 92 insertions(+), 5 deletions(-)

diff --git 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java
 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java
index 38d34fb..0f92bbf 100644
--- 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java
+++ 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java
@@ -809,6 +809,18 @@ public abstract class AbstractSurefireMojo
     private Map<String, String> jdkToolchain;
 
     /**
+     * How often output is forced to be flushed.
+     * Useful when there is no output for N ms but some data are buffered.
+     * It will trigger a flush each configured interval to ensure
+     * data don't stay blocked in a buffer.
+     * Setting it to 0 disable that feature.
+     *
+     * @since 3.0.0-M6
+     */
+    @Parameter( defaultValue = "0" )
+    private long outputFlushInterval;
+
+    /**
      *
      */
     @Component
@@ -1850,7 +1862,8 @@ public abstract class AbstractSurefireMojo
                                           testSuiteDefinition, 
providerProperties, null,
                                           false, cli, 
getSkipAfterFailureCount(),
                                           Shutdown.parameterOf( getShutdown() 
),
-                                          
getForkedProcessExitTimeoutInSeconds() );
+                                          
getForkedProcessExitTimeoutInSeconds(),
+                                          outputFlushInterval );
     }
 
     private static Map<String, String> toStringProperties( Properties 
properties )
diff --git 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/BooterSerializer.java
 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/BooterSerializer.java
index c8aee3c..6e62079 100644
--- 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/BooterSerializer.java
+++ 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/BooterSerializer.java
@@ -53,6 +53,7 @@ import static 
org.apache.maven.surefire.booter.BooterConstants.FORKTESTSET_PREFE
 import static 
org.apache.maven.surefire.booter.BooterConstants.INCLUDES_PROPERTY_PREFIX;
 import static 
org.apache.maven.surefire.booter.BooterConstants.ISTRIMSTACKTRACE;
 import static 
org.apache.maven.surefire.booter.BooterConstants.MAIN_CLI_OPTIONS;
+import static 
org.apache.maven.surefire.booter.BooterConstants.OUTPUT_FLUSH_INTERVAL_MS;
 import static org.apache.maven.surefire.booter.BooterConstants.PLUGIN_PID;
 import static org.apache.maven.surefire.booter.BooterConstants.PROCESS_CHECKER;
 import static 
org.apache.maven.surefire.booter.BooterConstants.PROVIDER_CONFIGURATION;
@@ -181,6 +182,10 @@ class BooterSerializer
             properties.addList( mainCliOptions, MAIN_CLI_OPTIONS );
         }
         properties.setNullableProperty( SYSTEM_EXIT_TIMEOUT, toString( 
providerConfiguration.getSystemExitTimeout() ) );
+        properties.setNullableProperty(
+            OUTPUT_FLUSH_INTERVAL_MS,
+            providerConfiguration.getOutputFlushInterval() == null
+                ? "0" : toString( 
providerConfiguration.getOutputFlushInterval() ) );
 
         File surefireTmpDir = forkConfiguration.getTempDirectory();
         boolean debug = forkConfiguration.isDebug();
diff --git 
a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerProviderConfigurationTest.java
 
b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerProviderConfigurationTest.java
index 7a08390..e62d1c3 100644
--- 
a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerProviderConfigurationTest.java
+++ 
b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerProviderConfigurationTest.java
@@ -279,7 +279,7 @@ public class BooterDeserializerProviderConfigurationTest
         RunOrderParameters runOrderParameters = new RunOrderParameters( 
RunOrder.DEFAULT, null );
         return new ProviderConfiguration( directoryScannerParameters, 
runOrderParameters, true, reporterConfiguration,
                 new TestArtifactInfo( "5.0", "ABC" ), testSuiteDefinition, new 
HashMap<String, String>(), TEST_TYPED,
-                readTestsFromInStream, cli, 0, Shutdown.DEFAULT, 0 );
+                readTestsFromInStream, cli, 0, Shutdown.DEFAULT, 0, 0L );
     }
 
     private StartupConfiguration getTestStartupConfiguration( 
ClassLoaderConfiguration classLoaderConfiguration )
diff --git 
a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerStartupConfigurationTest.java
 
b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerStartupConfigurationTest.java
index 669e73c..cfa9f55 100644
--- 
a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerStartupConfigurationTest.java
+++ 
b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerStartupConfigurationTest.java
@@ -200,7 +200,7 @@ public class BooterDeserializerStartupConfigurationTest
         RunOrderParameters runOrderParameters = new RunOrderParameters( 
RunOrder.DEFAULT, null );
         return new ProviderConfiguration( directoryScannerParameters, 
runOrderParameters, true, reporterConfiguration,
                 new TestArtifactInfo( "5.0", "ABC" ), testSuiteDefinition, new 
HashMap<String, String>(),
-                BooterDeserializerProviderConfigurationTest.TEST_TYPED, true, 
cli, 0, Shutdown.DEFAULT, 0 );
+                BooterDeserializerProviderConfigurationTest.TEST_TYPED, true, 
cli, 0, Shutdown.DEFAULT, 0, 0L );
     }
 
     private StartupConfiguration getTestStartupConfiguration( 
ClassLoaderConfiguration classLoaderConfiguration )
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..6e4a05d 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
@@ -39,6 +39,12 @@ abstract class AbstractNoninterruptibleWritableChannel 
implements WritableBuffer
     protected abstract void flushImpl() throws IOException;
 
     @Override
+    public final void flush() throws IOException
+    {
+        flushImpl();
+    }
+
+    @Override
     public final int write( ByteBuffer src ) throws IOException
     {
         return write( src, true );
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..cc8409c 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
@@ -33,4 +33,6 @@ import java.nio.channels.WritableByteChannel;
 public interface WritableBufferedByteChannel extends WritableByteChannel
 {
     void writeBuffered( ByteBuffer src ) throws IOException;
+
+    void flush() throws IOException;
 }
diff --git 
a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterConstants.java
 
b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterConstants.java
index fa664be..5c5c34f 100644
--- 
a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterConstants.java
+++ 
b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterConstants.java
@@ -59,4 +59,5 @@ public final class BooterConstants
     public static final String PLUGIN_PID = "pluginPid";
     public static final String PROCESS_CHECKER = "processChecker";
     public static final String FORK_NODE_CONNECTION_STRING = 
"forkNodeConnectionString";
+    public static final String OUTPUT_FLUSH_INTERVAL_MS = 
"outputFlushInterval";
 }
diff --git 
a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterDeserializer.java
 
b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterDeserializer.java
index 4ec8cec..0df4ead 100644
--- 
a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterDeserializer.java
+++ 
b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterDeserializer.java
@@ -131,11 +131,13 @@ public class BooterDeserializer
         Integer systemExitTimeout =
                 systemExitTimeoutAsString == null ? null : Integer.valueOf( 
systemExitTimeoutAsString );
 
+        long outputFlushIntervalMs = properties.getLongProperty( 
OUTPUT_FLUSH_INTERVAL_MS );
+
         return new ProviderConfiguration( dirScannerParams, runOrderParameters,
                                           properties.getBooleanProperty( 
FAILIFNOTESTS ), reporterConfiguration, testNg,
                                           testSuiteDefinition, 
properties.getProperties(), typeEncodedTestForFork,
                                           preferTestsFromInStream, 
fromStrings( cli ), failFastCount, shutdown,
-                                          systemExitTimeout );
+                                          systemExitTimeout, 
outputFlushIntervalMs );
     }
 
     public StartupConfiguration getStartupConfiguration()
diff --git 
a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedBooter.java
 
b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedBooter.java
index d8e3bfe..cd27162 100644
--- 
a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedBooter.java
+++ 
b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedBooter.java
@@ -27,6 +27,9 @@ import 
org.apache.maven.surefire.api.booter.ForkingReporterFactory;
 import org.apache.maven.surefire.api.booter.MasterProcessChannelDecoder;
 import org.apache.maven.surefire.api.booter.MasterProcessChannelEncoder;
 import org.apache.maven.surefire.api.booter.Shutdown;
+import org.apache.maven.surefire.api.util.internal.DaemonThreadFactory;
+import org.apache.maven.surefire.api.util.internal.WritableBufferedByteChannel;
+import org.apache.maven.surefire.booter.spi.LegacyMasterProcessChannelEncoder;
 import 
org.apache.maven.surefire.booter.spi.LegacyMasterProcessChannelProcessorFactory;
 import 
org.apache.maven.surefire.booter.spi.SurefireMasterProcessChannelProcessorFactory;
 import org.apache.maven.surefire.api.provider.CommandListener;
@@ -50,6 +53,7 @@ import java.lang.reflect.InvocationTargetException;
 import java.security.AccessControlException;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
+import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ScheduledThreadPoolExecutor;
 import java.util.concurrent.Semaphore;
@@ -100,6 +104,7 @@ public final class ForkedBooter
     private ForkingReporterFactory forkingReporterFactory;
     private StartupConfiguration startupConfiguration;
     private Object testSet;
+    private ScheduledExecutorService flusher;
 
     private void setupBooter( String tmpDir, String dumpFileName, String 
surefirePropsFileName,
                               String effectiveSystemPropertiesFileName )
@@ -129,6 +134,34 @@ public final class ForkedBooter
 
         flushEventChannelOnExit();
 
+        final Long outputFlushInterval = 
providerConfiguration.getOutputFlushInterval();
+        if ( outputFlushInterval != null && outputFlushInterval > 0
+            && LegacyMasterProcessChannelEncoder.class.isInstance( 
eventChannel ) )
+        {
+            final LegacyMasterProcessChannelEncoder enc = 
LegacyMasterProcessChannelEncoder.class.cast( eventChannel );
+            flusher = Executors.newSingleThreadScheduledExecutor( 
DaemonThreadFactory.newDaemonThreadFactory() );
+            flusher.scheduleAtFixedRate( new Runnable()
+            {
+                @Override
+                public void run()
+                {
+                    final WritableBufferedByteChannel c = 
WritableBufferedByteChannel.class.cast( enc.getOut() );
+                    try
+                    {
+                        if ( c.isOpen() )
+                        {
+                            c.flush();
+                        }
+
+                    }
+                    catch ( Exception e )
+                    {
+                        // no-op, not important, we tried anyway
+                    }
+                }
+            }, outputFlushInterval, outputFlushInterval, MILLISECONDS );
+        }
+
         forkingReporterFactory = createForkingReporterFactory();
         ConsoleLogger logger = (ConsoleLogger) 
forkingReporterFactory.createReporter();
         commandReader = new CommandReader( decoder, 
providerConfiguration.getShutdown(), logger );
@@ -237,6 +270,18 @@ public final class ForkedBooter
                 e.printStackTrace();
             }
         }
+        if ( flusher != null )
+        {
+            flusher.shutdownNow();
+            try
+            { // give a chance to finish sync, not that important if it fails
+                flusher.awaitTermination( 2, SECONDS );
+            }
+            catch ( InterruptedException e )
+            {
+                Thread.currentThread().interrupt();
+            }
+        }
     }
 
     private PingScheduler listenToShutdownCommands( String ppid, ConsoleLogger 
logger )
diff --git 
a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProviderConfiguration.java
 
b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProviderConfiguration.java
index 54b6187..77519c2 100644
--- 
a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProviderConfiguration.java
+++ 
b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProviderConfiguration.java
@@ -68,6 +68,8 @@ public class ProviderConfiguration
 
     private final Integer systemExitTimeout;
 
+    private final Long outputFlushInterval;
+
     @SuppressWarnings( "checkstyle:parameternumber" )
     public ProviderConfiguration( DirectoryScannerParameters 
directoryScannerParameters,
                                   RunOrderParameters runOrderParameters, 
boolean failIfNoTests,
@@ -75,7 +77,7 @@ public class ProviderConfiguration
                                   TestRequest testSuiteDefinition, Map<String, 
String> providerProperties,
                                   TypeEncodedValue typeEncodedTestSet, boolean 
readTestsFromInStream,
                                   List<CommandLineOption> mainCliOptions, int 
skipAfterFailureCount,
-                                  Shutdown shutdown, Integer systemExitTimeout 
)
+                                  Shutdown shutdown, Integer 
systemExitTimeout, Long outputFlushInterval )
     {
         this.runOrderParameters = runOrderParameters;
         this.providerProperties = providerProperties;
@@ -90,6 +92,12 @@ public class ProviderConfiguration
         this.skipAfterFailureCount = skipAfterFailureCount;
         this.shutdown = shutdown;
         this.systemExitTimeout = systemExitTimeout;
+        this.outputFlushInterval = outputFlushInterval;
+    }
+
+    public Long getOutputFlushInterval()
+    {
+        return outputFlushInterval;
     }
 
     public ReporterConfiguration getReporterConfiguration()
diff --git 
a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/LegacyMasterProcessChannelEncoder.java
 
b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/LegacyMasterProcessChannelEncoder.java
index 3cbc8eb..4c6d494 100644
--- 
a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/LegacyMasterProcessChannelEncoder.java
+++ 
b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/LegacyMasterProcessChannelEncoder.java
@@ -275,6 +275,11 @@ public class LegacyMasterProcessChannelEncoder implements 
MasterProcessChannelEn
         error( stackTraceWriter, trimStackTraces, BOOTERCODE_JVM_EXIT_ERROR, 
true );
     }
 
+    public WritableBufferedByteChannel getOut()
+    {
+        return out;
+    }
+
     private void error( StackTraceWriter stackTraceWriter, boolean 
trimStackTraces, ForkedProcessEventType event,
                         @SuppressWarnings( "SameParameterValue" ) boolean 
sendImmediately )
     {

Reply via email to