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();