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/01/22 23:02:40 UTC

[maven-surefire] branch maven2surefire-jvm-communication updated (a80fb9e -> ecc2b8c)

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

tibordigana pushed a change to branch maven2surefire-jvm-communication
in repository https://gitbox.apache.org/repos/asf/maven-surefire.git.


    omit a80fb9e  surefire JAR not created. Let's use the old version.
    omit 3ef39f9  investigate only Ubuntu
    omit 8a2b4d4  upload artifact
    omit 18bec24  cat dumps
    omit 05b12d0  skipped tests
    omit 03b6fbd  -fn
    omit 37bc76d  investigate dumps on Ubuntu
    omit 60999e1  removed '-DskipTests', '-DskipITs'
    omit f62bfe2  fixed few tests after bad merge
    omit 3baa67f  used reader of Command-s instead of InputStream
    omit 60c79d6  [SUREFIRE-1658] TCP/IP Channel for forked Surefire JVM. Extensions API and SPI. Polymorphism for remote and local process communication.
     add 6b3a796  [SUREFIRE-1742] Updated JUnit 4.12 to JUnit 4.13 in the unit/IT tests.
     add bfe1dbd  [README.md] set style=for-the-badge and added Maven icon
     add db835ce  keep the order of failures to rerun
     add acf7733  [SUREFIRE-1725] Surefire in JUnit Vintage mode distributes tests very unevenly between forks, causing poor parallelism
     add bbce91a  Use Surefire's StringUtils instead of JUnit's
     add d49f4da  Upgrade JUnit to 5.6 and JQwik to 1.2.2
     add 72aa2c1  Delete is{Not}Blank from internal StringUtils
     add 8868523  [SUREFIRE-1746] Dependencies for dynamic provider contain Maven artifacts from the MOJO plugin
     add fd986fd  The workaround on Ubuntu. It should be removed after M5.
     add 1cc6ef6  The workaround on Ubuntu. It should be removed after M5.
     new aaeccca  [SUREFIRE-1658] TCP/IP Channel for forked Surefire JVM. Extensions API and SPI. Polymorphism for remote and local process communication.
     new e2254d8  used reader of Command-s instead of InputStream
     new c070d9e  fixed few tests after bad merge
     new f0c9ae5  surefire JAR not created. Let's use the old version.
     new b413542  removed '-DskipTests', '-DskipITs'
     new 4be01d7  investigate dumps on Ubuntu
     new 83b7f52  -fn
     new 1d09662  skipped tests
     new a2850b0  cat dumps
     new a8fba24  upload artifact
     new f0fd66c  investigate only Ubuntu
     new ecc2b8c  Revert "investigate dumps on Ubuntu"

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (a80fb9e)
            \
             N -- N -- N   refs/heads/maven2surefire-jvm-communication (ecc2b8c)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

The 12 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .github/workflows/maven.yml                        |  12 +-
 README.md                                          |  21 ++-
 maven-failsafe-plugin/pom.xml                      |   5 -
 .../failsafe/util/FailsafeSummaryXmlUtils.java     |   2 +-
 .../plugin/surefire/AbstractSurefireMojo.java      |  17 +-
 .../surefire/SurefireDependencyResolver.java       |  78 ++++-----
 .../maven/plugin/surefire/SurefireProperties.java  |   2 +-
 .../surefire/booterclient/output/ForkClient.java   |   4 +-
 .../booterclient/output/ForkedChannelDecoder.java  |   4 +-
 .../maven/plugin/surefire/report/FileReporter.java |   2 +-
 .../surefire/report/StatelessXmlReporter.java      |   2 +-
 .../plugin/surefire/report/WrappedReportEntry.java |   2 +-
 .../maven/plugin/surefire/util/FileScanner.java    |   2 +-
 .../plugin/surefire/AbstractSurefireMojoTest.java  |  24 +--
 .../surefire/SurefireDependencyResolverTest.java   | 195 +++++++++------------
 .../src/site/apt/examples/junit-platform.apt.vm    |   2 +-
 maven-surefire-plugin/src/site/markdown/docker.md  |   2 +-
 pom.xml                                            |  12 +-
 .../org/apache/maven/surefire/booter/Command.java  |   2 +-
 .../maven/surefire/util/internal/ClassMethod.java  |   2 +-
 .../maven/surefire/util/internal/StringUtils.java  |  26 ---
 .../maven/surefire/booter/CommandReader.java       |   4 +-
 .../maven/surefire/booter/ProcessCheckerType.java  |   2 +-
 .../maven/surefire/booter/PropertiesWrapper.java   |   2 +-
 .../booter/SurefireBooterForkException.java        |   2 +-
 .../surefire/extensions/ConsoleOutputReporter.java |   2 +-
 .../surefire/extensions/StatelessReporter.java     |   2 +-
 .../its/JUnit47RerunFailingTestWithCucumberIT.java |   2 +-
 .../surefire/its/JUnit4RerunFailingTestsIT.java    |  40 ++---
 .../maven/surefire/its/JUnit4VersionsIT.java       |   4 +-
 .../apache/maven/surefire/its/JUnitPlatformIT.java |   4 +-
 .../apache/maven/surefire/its/JUnitVersion.java    |   3 +-
 .../resources/assumpationFailureReport/pom.xml     |   2 +-
 .../src/test/resources/java9-full-api/pom.xml      |   2 +-
 surefire-its/src/test/resources/junit4/pom.xml     |   2 +-
 surefire-logger-api/pom.xml                        |   1 +
 .../common/junit4/JUnit4RunListenerFactory.java    |   2 +-
 .../surefire/common/junit48/FilterFactory.java     |   2 +-
 .../junitplatform/JUnitPlatformProvider.java       |  44 +++--
 .../surefire/junitplatform/RunListenerAdapter.java |   1 -
 .../junitplatform/JUnitPlatformProviderTest.java   |  41 +++++
 .../surefire/testng/TestNGDirectoryTestSuite.java  |   2 +-
 .../maven/surefire/testng/TestNGExecutor.java      |   2 +-
 43 files changed, 292 insertions(+), 294 deletions(-)


[maven-surefire] 12/12: Revert "investigate dumps on Ubuntu"

Posted by ti...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit ecc2b8ca612b29f779cbd6f075daf7cf7c01189d
Author: tibordigana <ti...@apache.org>
AuthorDate: Thu Jan 23 00:02:09 2020 +0100

    Revert "investigate dumps on Ubuntu"
    
    This reverts commit 4be01d7d
---
 .github/workflows/maven.yml | 12 ++----------
 1 file changed, 2 insertions(+), 10 deletions(-)

diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index d9e509c..9bfd417 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -24,7 +24,7 @@ jobs:
 
     strategy:
       matrix:
-        os: [ubuntu-latest]
+        os: [ubuntu-latest, windows-latest, macOS-latest]
       fail-fast: false
 
     runs-on: ${{ matrix.os }}
@@ -39,12 +39,4 @@ jobs:
           java-version: 1.8
 
       - name: Build with Maven
-        run: mvn install -e -B -V -nsu --no-transfer-progress -fn -f surefire-logger-api/pom.xml -X
-
-      - uses: actions/upload-artifact@v1
-        with:
-          name: my-artifact
-          path: surefire-logger-api/target
-
-      - name: cat dump
-        run: cd surefire-logger-api/target/surefire-reports && cat *
+        run: mvn install -e -B -V -nsu --no-transfer-progress -P run-its
\ No newline at end of file


[maven-surefire] 02/12: used reader of Command-s instead of InputStream

Posted by ti...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit e2254d892775f77204870c1448bd2bb797e42241
Author: tibordigana <ti...@apache.org>
AuthorDate: Sun Jan 12 04:49:58 2020 +0100

    used reader of Command-s instead of InputStream
---
 .../AbstractClasspathForkConfiguration.java        |  3 +-
 .../surefire/booterclient/BooterSerializer.java    |  2 +-
 .../booterclient/ClasspathForkConfiguration.java   |  3 +-
 .../booterclient/JarManifestForkConfiguration.java |  3 +-
 .../ModularClasspathForkConfiguration.java         |  3 +-
 .../output/NativeStdErrStreamConsumer.java         |  2 +-
 .../extensions/NetworkingProcessExecutor.java      | 16 ++++-----
 .../surefire/extensions/PipeProcessExecutor.java   | 17 +++++----
 .../plugin/surefire/extensions/StdErrAdapter.java  | 42 ----------------------
 .../surefire/extensions/SurefireForkChannel.java   | 17 +++++++--
 .../AbstractSurefireMojoJava7PlusTest.java         |  6 ++++
 .../plugin/surefire/AbstractSurefireMojoTest.java  |  6 ++++
 .../booterclient/ForkConfigurationTest.java        |  4 ++-
 .../surefire/booterclient/ForkStarterTest.java     | 12 ++++---
 .../plugin/surefire/booterclient/MainClass.java    | 14 +++-----
 .../TestLessInputStreamBuilderTest.java            | 20 +++++++----
 .../TestProvidingInputStreamTest.java              | 40 ++++++++++-----------
 .../maven/surefire/extensions/CommandReader.java   |  4 ++-
 .../surefire/extensions/ExecutableCommandline.java | 14 ++++----
 .../surefire/extensions/StdErrStreamLine.java      |  5 +--
 .../surefire/extensions/util/StreamFeeder.java     | 20 +++++++----
 .../extensions/util/CommandlineExecutorTest.java   | 29 ++++++++++++---
 22 files changed, 152 insertions(+), 130 deletions(-)

diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/AbstractClasspathForkConfiguration.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/AbstractClasspathForkConfiguration.java
index 08a8373..f8e08ea 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/AbstractClasspathForkConfiguration.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/AbstractClasspathForkConfiguration.java
@@ -54,7 +54,8 @@ abstract class AbstractClasspathForkConfiguration
                                         @Nonnull ForkNodeFactory forkNodeFactory )
     {
         super( bootClasspath, tempDirectory, debugLine, workingDirectory, modelProperties, argLine,
-                environmentVariables, excludedEnvironmentVariables, debug, forkCount, reuseForks, pluginPlatform, log, forkNodeFactory );
+            environmentVariables, excludedEnvironmentVariables, debug, forkCount, reuseForks, pluginPlatform, log,
+            forkNodeFactory );
     }
 
     @Override
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 b166cc2..9a04326 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
@@ -107,7 +107,7 @@ class BooterSerializer
         throws IOException
     {
         SurefireProperties properties = new SurefireProperties( sourceProperties );
-        properties.setProperty( FORK_NODE_CONNECTION_STRING, forkNodeConnectionString );
+        properties.setNullableProperty( FORK_NODE_CONNECTION_STRING, forkNodeConnectionString );
         properties.setProperty( PLUGIN_PID, pid );
 
         AbstractPathConfiguration cp = startupConfiguration.getClasspathConfiguration();
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ClasspathForkConfiguration.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ClasspathForkConfiguration.java
index 94b7ee3..9c906c4 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ClasspathForkConfiguration.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ClasspathForkConfiguration.java
@@ -53,7 +53,8 @@ public final class ClasspathForkConfiguration
                                        @Nonnull ForkNodeFactory forkNodeFactory )
     {
         super( bootClasspath, tempDirectory, debugLine, workingDirectory, modelProperties, argLine,
-                environmentVariables, excludedEnvironmentVariables, debug, forkCount, reuseForks, pluginPlatform, log, forkNodeFactory );
+            environmentVariables, excludedEnvironmentVariables, debug, forkCount, reuseForks, pluginPlatform, log,
+            forkNodeFactory );
     }
 
     @Override
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/JarManifestForkConfiguration.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/JarManifestForkConfiguration.java
index 281118b..382bec6 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/JarManifestForkConfiguration.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/JarManifestForkConfiguration.java
@@ -75,7 +75,8 @@ public final class JarManifestForkConfiguration
                                          @Nonnull ForkNodeFactory forkNodeFactory )
     {
         super( bootClasspath, tempDirectory, debugLine, workingDirectory, modelProperties, argLine,
-                environmentVariables, excludedEnvironmentVariables, debug, forkCount, reuseForks, pluginPlatform, log, forkNodeFactory );
+            environmentVariables, excludedEnvironmentVariables, debug, forkCount, reuseForks, pluginPlatform, log,
+            forkNodeFactory );
     }
 
     @Override
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ModularClasspathForkConfiguration.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ModularClasspathForkConfiguration.java
index 67a977c..d659a6c 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ModularClasspathForkConfiguration.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ModularClasspathForkConfiguration.java
@@ -72,7 +72,8 @@ public class ModularClasspathForkConfiguration
                                               @Nonnull ForkNodeFactory forkNodeFactory )
     {
         super( bootClasspath, tempDirectory, debugLine, workingDirectory, modelProperties, argLine,
-                environmentVariables, excludedEnvironmentVariables, debug, forkCount, reuseForks, pluginPlatform, log, forkNodeFactory );
+            environmentVariables, excludedEnvironmentVariables, debug, forkCount, reuseForks, pluginPlatform, log,
+            forkNodeFactory );
     }
 
     @Override
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/NativeStdErrStreamConsumer.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/NativeStdErrStreamConsumer.java
index 6082096..8e4f6f0 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/NativeStdErrStreamConsumer.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/NativeStdErrStreamConsumer.java
@@ -40,7 +40,7 @@ public final class NativeStdErrStreamConsumer
     }
 
     @Override
-    public void handleLine( String line )
+    public void consumeLine( String line )
     {
         InPluginProcessDumpSingleton.getSingleton()
                 .dumpStreamText( line, defaultReporterFactory.getReportsDirectory() );
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/NetworkingProcessExecutor.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/NetworkingProcessExecutor.java
index 71b75cc..eecb5ca 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/NetworkingProcessExecutor.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/NetworkingProcessExecutor.java
@@ -61,12 +61,12 @@ final class NetworkingProcessExecutor implements ExecutableCommandline
 
     @Nonnull
     @Override
-    public <T> Callable<Integer> executeCommandLineAsCallable( @Nonnull T cli,
-                                                               @Nonnull final CommandReader commands,
-                                                               @Nonnull final EventHandler events,
-                                                               StdOutStreamLine stdOut,
-                                                               StdErrStreamLine stdErr,
-                                                               @Nonnull Runnable runAfterProcessTermination )
+    public Callable<Integer> executeCommandLineAsCallable( @Nonnull Commandline cli,
+                                                           @Nonnull final CommandReader commands,
+                                                           @Nonnull final EventHandler events,
+                                                           StdOutStreamLine stdOut,
+                                                           StdErrStreamLine stdErr,
+                                                           @Nonnull Runnable runAfterProcessTermination )
             throws Exception
     {
         server.accept( null, new CompletionHandler<AsynchronousSocketChannel, Object>()
@@ -154,8 +154,8 @@ final class NetworkingProcessExecutor implements ExecutableCommandline
             }
         } );
 
-        return CommandLineUtils.executeCommandLineAsCallable( (Commandline) cli, null,
-                new StdOutAdapter( stdOut ), new StdErrAdapter( stdErr ), 0, runAfterProcessTermination, US_ASCII );
+        return CommandLineUtils.executeCommandLineAsCallable( cli, null,
+                new StdOutAdapter( stdOut ), stdErr, 0, runAfterProcessTermination, US_ASCII );
     }
 
     private static InputStream toInputStream( final AsynchronousSocketChannel client )
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/PipeProcessExecutor.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/PipeProcessExecutor.java
index f6e942b..ad3bcfc 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/PipeProcessExecutor.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/PipeProcessExecutor.java
@@ -59,17 +59,16 @@ final class PipeProcessExecutor
 {
     @Override
     @Nonnull
-    public <T> Callable<Integer> executeCommandLineAsCallable( @Nonnull T cli,
-                                                               @Nonnull CommandReader commands,
-                                                               @Nonnull EventHandler events,
-                                                               StdOutStreamLine stdOut,
-                                                               StdErrStreamLine stdErr,
-                                                               @Nonnull Runnable runAfterProcessTermination )
+    public Callable<Integer> executeCommandLineAsCallable( @Nonnull Commandline cli,
+                                                           @Nonnull CommandReader commands,
+                                                           @Nonnull EventHandler events,
+                                                           StdOutStreamLine stdOut,
+                                                           StdErrStreamLine stdErr,
+                                                           @Nonnull Runnable runAfterProcessTermination )
             throws Exception
     {
-        return CommandLineUtils.executeCommandLineAsCallable( (Commandline) cli, new CommandReaderAdapter( commands ),
-                new EventHandlerAdapter( events ), new StdErrAdapter( stdErr ),
-                0, runAfterProcessTermination, US_ASCII );
+        return CommandLineUtils.executeCommandLineAsCallable( cli, new CommandReaderAdapter( commands ),
+                new EventHandlerAdapter( events ), stdErr, 0, runAfterProcessTermination, US_ASCII );
     }
 
     private static class EventHandlerAdapter implements StreamConsumer
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/StdErrAdapter.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/StdErrAdapter.java
deleted file mode 100644
index 52d352a..0000000
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/StdErrAdapter.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package org.apache.maven.plugin.surefire.extensions;
-
-/*
- * 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.extensions.StdErrStreamLine;
-import org.apache.maven.surefire.shared.utils.cli.StreamConsumer;
-
-/**
- *
- */
-final class StdErrAdapter implements StreamConsumer
-{
-    private final StdErrStreamLine stdErr;
-
-    StdErrAdapter( StdErrStreamLine stdErr )
-    {
-        this.stdErr = stdErr;
-    }
-
-    @Override
-    public void consumeLine( String line )
-    {
-        stdErr.handleLine( line );
-    }
-}
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireForkChannel.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireForkChannel.java
index fb8f789..2238898 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireForkChannel.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireForkChannel.java
@@ -25,6 +25,7 @@ import org.apache.maven.surefire.extensions.ForkChannel;
 import javax.annotation.Nonnull;
 import java.io.IOException;
 import java.net.InetSocketAddress;
+import java.net.SocketOption;
 import java.nio.channels.AsynchronousServerSocketChannel;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -49,13 +50,23 @@ final class SurefireForkChannel implements ForkChannel
     {
         executorService = Executors.newCachedThreadPool( newDaemonThreadFactory() );
         server = open( withThreadPool( executorService ) );
-        server.setOption( SO_REUSEADDR, true );
-        server.setOption( TCP_NODELAY, true );
-        server.setOption( SO_KEEPALIVE, true );
+        setTrueOptions( SO_REUSEADDR, TCP_NODELAY, SO_KEEPALIVE );
         server.bind( new InetSocketAddress( 0 ) );
         serverPort = ( (InetSocketAddress) server.getLocalAddress() ).getPort();
     }
 
+    @SafeVarargs
+    private final void setTrueOptions( SocketOption<Boolean>... options ) throws IOException
+    {
+        for ( SocketOption<Boolean> option : options )
+        {
+            if ( server.supportedOptions().contains( option ) )
+            {
+                server.setOption( option, true );
+            }
+        }
+    }
+
     @Override
     public String getForkNodeConnectionString()
     {
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoJava7PlusTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoJava7PlusTest.java
index bc722e3..da9791a 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoJava7PlusTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoJava7PlusTest.java
@@ -633,6 +633,12 @@ public class AbstractSurefireMojoJava7PlusTest
         }
 
         @Override
+        protected ForkNodeFactory getForkNode()
+        {
+            return null;
+        }
+
+        @Override
         protected Artifact getMojoArtifact()
         {
             return null;
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoTest.java
index e70b951..207648f 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoTest.java
@@ -2122,6 +2122,12 @@ public class AbstractSurefireMojoTest
         }
 
         @Override
+        protected ForkNodeFactory getForkNode()
+        {
+            return null;
+        }
+
+        @Override
         protected Artifact getMojoArtifact()
         {
             return new DefaultArtifact( "org.apache.maven.surefire", "maven-surefire-plugin", createFromVersion( "1" ),
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkConfigurationTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkConfigurationTest.java
index 16564a3..8d806ca 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkConfigurationTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkConfigurationTest.java
@@ -45,7 +45,9 @@ import static java.util.Collections.singletonList;
 import static org.apache.maven.surefire.booter.Classpath.emptyClasspath;
 import static org.apache.maven.surefire.booter.ProcessCheckerType.ALL;
 import static org.fest.util.Files.temporaryFolder;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 import static org.mockito.Mockito.mock;
 
 /**
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkStarterTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkStarterTest.java
index e0874f0..a7ab4d7 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkStarterTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkStarterTest.java
@@ -21,12 +21,13 @@ package org.apache.maven.plugin.surefire.booterclient;
 
 import org.apache.maven.plugin.surefire.StartupReportConfiguration;
 import org.apache.maven.plugin.surefire.SurefireProperties;
-import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.AbstractForkInputStream;
+import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.AbstractCommandReader;
 import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.OutputStreamFlushableCommandline;
 import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.TestLessInputStream;
 import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.TestLessInputStream.TestLessInputStreamBuilder;
 import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.TestProvidingInputStream;
 import org.apache.maven.plugin.surefire.booterclient.output.ForkClient;
+import org.apache.maven.plugin.surefire.extensions.LegacyForkNodeFactory;
 import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
 import org.apache.maven.plugin.surefire.report.DefaultReporterFactory;
 import org.apache.maven.surefire.booter.AbstractPathConfiguration;
@@ -37,6 +38,7 @@ import org.apache.maven.surefire.booter.ProviderConfiguration;
 import org.apache.maven.surefire.booter.Shutdown;
 import org.apache.maven.surefire.booter.StartupConfiguration;
 import org.apache.maven.surefire.booter.SurefireBooterForkException;
+import org.apache.maven.surefire.extensions.ForkNodeFactory;
 import org.apache.maven.surefire.report.ReporterConfiguration;
 import org.apache.maven.surefire.shared.compress.archivers.zip.Zip64Mode;
 import org.apache.maven.surefire.shared.compress.archivers.zip.ZipArchiveEntry;
@@ -170,12 +172,12 @@ public class ForkStarterTest
         e.expectMessage( containsString( "VM crash or System.exit called?" ) );
 
         Class<?>[] types = {Object.class, PropertiesWrapper.class, ForkClient.class, SurefireProperties.class,
-            int.class, AbstractForkInputStream.class, boolean.class};
+            int.class, AbstractCommandReader.class, ForkNodeFactory.class, boolean.class};
         TestProvidingInputStream testProvidingInputStream = new TestProvidingInputStream( new ArrayDeque<String>() );
         invokeMethod( forkStarter, "fork", types, null,
             new PropertiesWrapper( Collections.<String, String>emptyMap() ),
             new ForkClient( reporterFactory, null, logger, new AtomicBoolean(), 1 ),
-            new SurefireProperties(), 1, testProvidingInputStream, true );
+            new SurefireProperties(), 1, testProvidingInputStream, new LegacyForkNodeFactory(), true );
         testProvidingInputStream.close();
     }
 
@@ -224,12 +226,12 @@ public class ForkStarterTest
         DefaultReporterFactory reporterFactory = new DefaultReporterFactory( startupReportConfiguration, logger, 1 );
 
         Class<?>[] types = {Object.class, PropertiesWrapper.class, ForkClient.class, SurefireProperties.class,
-            int.class, AbstractForkInputStream.class, boolean.class};
+            int.class, AbstractCommandReader.class, ForkNodeFactory.class, boolean.class};
         TestLessInputStream testLessInputStream = new TestLessInputStreamBuilder().build();
         invokeMethod( forkStarter, "fork", types, null,
             new PropertiesWrapper( Collections.<String, String>emptyMap() ),
             new ForkClient( reporterFactory, testLessInputStream, logger, new AtomicBoolean(), 1 ),
-            new SurefireProperties(), 1, testLessInputStream, true );
+            new SurefireProperties(), 1, testLessInputStream, new LegacyForkNodeFactory(), true );
         testLessInputStream.close();
     }
 
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/MainClass.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/MainClass.java
index d90a128..f546d66 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/MainClass.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/MainClass.java
@@ -34,15 +34,11 @@ public class MainClass
         }
         else
         {
-            System.out.println( ":maven:surefire:std:out:bye" );
-            if ( System.in.read() == 0
-                && System.in.read() == 0
-                && System.in.read() == 0
-                && System.in.read() == 5
-                && System.in.read() == 0
-                && System.in.read() == 0
-                && System.in.read() == 0
-                && System.in.read() == 0 )
+            System.out.println( ":maven:surefire:std:out:bye\n" );
+            String byeAck = ":maven-surefire-std-out:bye-ack:";
+            byte[] cmd = new byte[byeAck.length()];
+            int len = System.in.read( cmd );
+            if ( len != -1 && new String( cmd, 0, len ).equals( byeAck ) )
             {
                 System.exit( 0 );
             }
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestLessInputStreamBuilderTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestLessInputStreamBuilderTest.java
index 39789ac..d14014a 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestLessInputStreamBuilderTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestLessInputStreamBuilderTest.java
@@ -32,8 +32,8 @@ import java.io.InputStream;
 import java.util.Iterator;
 import java.util.NoSuchElementException;
 
-import static org.apache.maven.plugin.surefire.booterclient.lazytestprovider.TestLessInputStream.TestLessInputStreamBuilder;
-import static org.apache.maven.surefire.booter.Command.NOOP;
+import static org.apache.maven.plugin.surefire.booterclient.lazytestprovider
+    .TestLessInputStream.TestLessInputStreamBuilder;
 import static org.apache.maven.surefire.booter.Command.SKIP_SINCE_NEXT_TEST;
 import static org.apache.maven.surefire.booter.MasterProcessCommand.SHUTDOWN;
 import static org.apache.maven.surefire.booter.Shutdown.EXIT;
@@ -88,7 +88,7 @@ public class TestLessInputStreamBuilderTest
         assertThat( is.availablePermits(), is( 1 ) );
         is.beforeNextCommand();
         assertThat( is.availablePermits(), is( 0 ) );
-        assertThat( is.nextCommand(), is( NOOP ) );
+        assertThat( is.nextCommand(), is( Command.NOOP ) );
         assertThat( is.availablePermits(), is( 0 ) );
         e.expect( NoSuchElementException.class );
         is.nextCommand();
@@ -106,7 +106,7 @@ public class TestLessInputStreamBuilderTest
         assertThat( is.availablePermits(), is( 2 ) );
         is.beforeNextCommand();
         assertThat( is.availablePermits(), is( 1 ) );
-        assertThat( is.nextCommand(), is( NOOP ) );
+        assertThat( is.nextCommand(), is( Command.NOOP ) );
         assertThat( is.availablePermits(), is( 1 ) );
         builder.getCachableCommands().skipSinceNextTest();
         assertThat( is.availablePermits(), is( 1 ) );
@@ -124,7 +124,7 @@ public class TestLessInputStreamBuilderTest
         builder.getCachableCommands().shutdown( EXIT );
         assertThat( is.availablePermits(), is( 2 ) );
         is.beforeNextCommand();
-        assertThat( is.nextCommand(), is( NOOP ) );
+        assertThat( is.nextCommand(), is( Command.NOOP ) );
         assertThat( is.availablePermits(), is( 1 ) );
         is.beforeNextCommand();
         assertThat( is.nextCommand().getCommandType(), is( SHUTDOWN ) );
@@ -151,7 +151,15 @@ public class TestLessInputStreamBuilderTest
                 {
                     idx = 0;
                     Command cmd = pluginIs.readNextCommand();
-                    buffer = cmd == null ? null : cmd.getCommandType().encode();
+                    if ( cmd == null )
+                    {
+                        buffer = null;
+                    }
+                    else
+                    {
+                        MasterProcessCommand cmdType = cmd.getCommandType();
+                        buffer = cmdType.hasDataType() ? cmdType.encode( cmd.getData() ) : cmdType.encode();
+                    }
                 }
 
                 if ( buffer != null )
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStreamTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStreamTest.java
index f863be1..66b4218 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStreamTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStreamTest.java
@@ -40,6 +40,7 @@ import static org.apache.maven.surefire.booter.MasterProcessCommand.NOOP;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.Matchers.nullValue;
 import static org.junit.Assert.assertTrue;
 
 /**
@@ -50,6 +51,7 @@ import static org.junit.Assert.assertTrue;
  */
 public class TestProvidingInputStreamTest
 {
+    private static final int WAIT_LOOPS = 100;
     @Test
     public void closedStreamShouldReturnNullAsEndOfStream()
         throws IOException
@@ -101,15 +103,16 @@ public class TestProvidingInputStreamTest
             }
         } ).start();
 
-        StringBuilder stream = new StringBuilder();
-        for ( int i = 0; i < 82; i++ )
-        {
-            Command cmd = is.readNextCommand();
-            assertThat( cmd.getData(), is( nullValue() ) );
-            stream.append( new String( cmd.getCommandType().encode(), US_ASCII ) );
-        }
-        assertThat( stream.toString(),
-                is( ":maven-surefire-std-out:testset-finished::maven-surefire-std-out:testset-finished:" ) );
+        Command cmd = is.readNextCommand();
+        assertThat( cmd.getData(), is( nullValue() ) );
+        String stream = new String( cmd.getCommandType().encode(), US_ASCII );
+
+        cmd = is.readNextCommand();
+        assertThat( cmd.getData(), is( nullValue() ) );
+        stream += new String( cmd.getCommandType().encode(), US_ASCII );
+
+        assertThat( stream,
+            is( ":maven-surefire-std-out:testset-finished::maven-surefire-std-out:testset-finished:" ) );
 
         boolean emptyStream = isInputStreamEmpty( is );
 
@@ -134,15 +137,8 @@ public class TestProvidingInputStreamTest
             }
         } ).start();
 
-        StringBuilder stream = new StringBuilder();
-        for ( int i = 0; i < 43; i++ )
-        {
-            Command cmd = is.readNextCommand();
-            assertThat( cmd.getData(), is( nullValue() ) );
-            stream.append( new String( is.readNextCommand().getCommandType().encode(), US_ASCII ) );
-        }
-        assertThat( stream.toString(),
-                is( ":maven-surefire-std-out:run-testclass:Test:" ) );
+        Command cmd = is.readNextCommand();
+        assertThat( cmd.getData(), is( "Test" ) );
 
         is.close();
     }
@@ -223,7 +219,10 @@ public class TestProvidingInputStreamTest
                 {
                     Throwable cause = e.getCause();
                     Throwable err = cause == null ? e : cause;
-                    System.err.println( err.toString() );
+                    if ( !( err instanceof InterruptedException ) )
+                    {
+                        System.err.println( err.toString() );
+                    }
                 }
             }
         } );
@@ -234,7 +233,8 @@ public class TestProvidingInputStreamTest
         {
             sleep( 100L );
             state = t.getState();
-        } while ( state == State.NEW && loops++ < 200 );
+        }
+        while ( state == State.NEW && loops++ < WAIT_LOOPS );
         t.interrupt();
         return state == State.WAITING || state == State.TIMED_WAITING;
     }
diff --git a/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/CommandReader.java b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/CommandReader.java
index 8dd9d96..08fe5ed 100644
--- a/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/CommandReader.java
+++ b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/CommandReader.java
@@ -21,6 +21,7 @@ package org.apache.maven.surefire.extensions;
 
 import org.apache.maven.surefire.booter.Command;
 
+import java.io.Closeable;
 import java.io.IOException;
 
 /**
@@ -29,7 +30,7 @@ import java.io.IOException;
  * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
  * @since 3.0.0-M4
  */
-public interface CommandReader
+public interface CommandReader extends Closeable
 {
 
     /**
@@ -38,6 +39,7 @@ public interface CommandReader
      * @return the command, or null if closed
      */
     Command readNextCommand() throws IOException;
+    @Override
     void close();
     boolean isClosed();
     void tryFlush() throws IOException;
diff --git a/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/ExecutableCommandline.java b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/ExecutableCommandline.java
index b373002..ed7a8ec 100644
--- a/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/ExecutableCommandline.java
+++ b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/ExecutableCommandline.java
@@ -19,6 +19,8 @@ package org.apache.maven.surefire.extensions;
  * under the License.
  */
 
+import org.apache.maven.surefire.shared.utils.cli.Commandline;
+
 import javax.annotation.Nonnull;
 import java.util.concurrent.Callable;
 
@@ -28,11 +30,11 @@ import java.util.concurrent.Callable;
 public interface ExecutableCommandline
 {
     @Nonnull
-    <T> Callable<Integer> executeCommandLineAsCallable( @Nonnull T cli,
-                                                        @Nonnull CommandReader commands,
-                                                        @Nonnull EventHandler events,
-                                                        StdOutStreamLine stdOut,
-                                                        StdErrStreamLine stdErr,
-                                                        @Nonnull Runnable runAfterProcessTermination )
+    Callable<Integer> executeCommandLineAsCallable( @Nonnull Commandline cli,
+                                                    @Nonnull CommandReader commands,
+                                                    @Nonnull EventHandler events,
+                                                    StdOutStreamLine stdOut,
+                                                    StdErrStreamLine stdErr,
+                                                    @Nonnull Runnable runAfterProcessTermination )
             throws Exception;
 }
diff --git a/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/StdErrStreamLine.java b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/StdErrStreamLine.java
index be444ae..f0ae082 100644
--- a/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/StdErrStreamLine.java
+++ b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/StdErrStreamLine.java
@@ -19,10 +19,11 @@ package org.apache.maven.surefire.extensions;
  * under the License.
  */
 
+import org.apache.maven.surefire.shared.utils.cli.StreamConsumer;
+
 /**
  * The line handler of forked process standard-error.
  */
-public interface StdErrStreamLine
+public interface StdErrStreamLine extends StreamConsumer
 {
-    void handleLine( String line );
 }
diff --git a/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/util/StreamFeeder.java b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/util/StreamFeeder.java
index ca8eb8e..9305d07 100644
--- a/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/util/StreamFeeder.java
+++ b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/util/StreamFeeder.java
@@ -19,10 +19,13 @@ package org.apache.maven.surefire.extensions.util;
  * under the License.
  */
 
+import org.apache.maven.surefire.booter.Command;
+import org.apache.maven.surefire.booter.MasterProcessCommand;
+import org.apache.maven.surefire.extensions.CommandReader;
+
 import javax.annotation.Nonnull;
 import java.io.Closeable;
 import java.io.IOException;
-import java.io.InputStream;
 import java.nio.ByteBuffer;
 import java.nio.channels.ClosedChannelException;
 import java.nio.channels.NonWritableChannelException;
@@ -34,30 +37,33 @@ import java.nio.channels.WritableByteChannel;
 public class StreamFeeder extends Thread implements Closeable
 {
     private final WritableByteChannel channel;
-    private final InputStream is;
+    private final CommandReader commandReader;
 
     private volatile boolean disabled;
     private volatile Throwable exception;
 
-    public StreamFeeder( @Nonnull String threadName, @Nonnull WritableByteChannel channel, @Nonnull InputStream is )
+    public StreamFeeder( @Nonnull String threadName, @Nonnull WritableByteChannel channel,
+                         @Nonnull CommandReader commandReader )
     {
         setName( threadName );
         setDaemon( true );
         this.channel = channel;
-        this.is = is;
+        this.commandReader = commandReader;
     }
 
     @Override
+    @SuppressWarnings( "checkstyle:innerassignment" )
     public void run()
     {
         try ( WritableByteChannel c = channel )
         {
-            for ( int data = is.read(); data != -1; data = is.read()  )
+            for ( Command cmd; ( cmd = commandReader.readNextCommand() ) != null; )
             {
                 if ( !disabled )
                 {
-                    // todo use CommandReader interface instead of InputStream. Then we would write ByteBuffer.
-                    c.write( ByteBuffer.wrap( new byte[] {(byte) data} ) );
+                    MasterProcessCommand cmdType = cmd.getCommandType();
+                    byte[] data = cmdType.hasDataType() ? cmdType.encode( cmd.getData() ) : cmdType.encode();
+                    c.write( ByteBuffer.wrap( data ) );
                 }
             }
         }
diff --git a/surefire-extensions-api/src/test/java/org/apache/maven/surefire/extensions/util/CommandlineExecutorTest.java b/surefire-extensions-api/src/test/java/org/apache/maven/surefire/extensions/util/CommandlineExecutorTest.java
index fd99059..fc919d2 100644
--- a/surefire-extensions-api/src/test/java/org/apache/maven/surefire/extensions/util/CommandlineExecutorTest.java
+++ b/surefire-extensions-api/src/test/java/org/apache/maven/surefire/extensions/util/CommandlineExecutorTest.java
@@ -19,6 +19,8 @@ package org.apache.maven.surefire.extensions.util;
  * under the License.
  */
 
+import org.apache.maven.surefire.booter.Command;
+import org.apache.maven.surefire.extensions.CommandReader;
 import org.apache.maven.surefire.shared.utils.cli.Commandline;
 import org.apache.maven.surefire.shared.utils.cli.StreamConsumer;
 import org.junit.After;
@@ -26,7 +28,6 @@ import org.junit.Before;
 import org.junit.Test;
 
 import java.io.Closeable;
-import java.io.InputStream;
 import java.nio.file.Paths;
 
 import static org.apache.maven.surefire.shared.lang3.SystemUtils.IS_OS_WINDOWS;
@@ -90,15 +91,33 @@ public class CommandlineExecutorTest
         exec = new CommandlineExecutor( cli, countdownCloseable );
         streams = exec.execute();
         StreamConsumer consumer = mock( StreamConsumer.class );
-        InputStream is = new InputStream()
+        CommandReader commandReader = new CommandReader()
         {
             @Override
-            public int read()
+            public Command readNextCommand()
             {
-                return -1;
+                return null;
+            }
+
+            @Override
+            public void close()
+            {
+
+            }
+
+            @Override
+            public boolean isClosed()
+            {
+                return false;
+            }
+
+            @Override
+            public void tryFlush()
+            {
+
             }
         };
-        StreamFeeder in = new StreamFeeder( "std-in-fork-1", streams.getStdInChannel(), is );
+        StreamFeeder in = new StreamFeeder( "std-in-fork-1", streams.getStdInChannel(), commandReader );
         in.start();
         out = new LineConsumerThread( "std-out-fork-1", streams.getStdOutChannel(), consumer, countdownCloseable );
         out.start();


[maven-surefire] 09/12: cat dumps

Posted by ti...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit a2850b091378300cd008e9b2b3bd810038ee76a9
Author: tibordigana <ti...@apache.org>
AuthorDate: Mon Jan 13 01:36:39 2020 +0100

    cat dumps
---
 .github/workflows/maven.yml | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index c4d1218..b0f38e0 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -39,10 +39,7 @@ jobs:
           java-version: 1.8
 
       - name: Build with Maven
-        run: mvn install -e -B -V -nsu --no-transfer-progress -fn -DskipTests
+        run: mvn install -e -B -V -nsu --no-transfer-progress -fn -f surefire-logger-api/pom.xml
 
       - name: cat dump
-        run: cat surefire-logger-api/target/surefire-reports/*.dump
-
-      - name: cat dumpstream
-        run: cat surefire-logger-api/target/surefire-reports/*.dumpstream
+        run: cd surefire-logger-api/target/surefire-reports && cat *


[maven-surefire] 08/12: skipped tests

Posted by ti...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 1d09662402d15ce531cdcd73cee1d9dded4b69bc
Author: tibordigana <ti...@apache.org>
AuthorDate: Mon Jan 13 01:28:55 2020 +0100

    skipped tests
---
 .github/workflows/maven.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index 26b7bf0..c4d1218 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -39,7 +39,7 @@ jobs:
           java-version: 1.8
 
       - name: Build with Maven
-        run: mvn install -e -B -V -nsu --no-transfer-progress -P run-its -fn
+        run: mvn install -e -B -V -nsu --no-transfer-progress -fn -DskipTests
 
       - name: cat dump
         run: cat surefire-logger-api/target/surefire-reports/*.dump


[maven-surefire] 10/12: upload artifact

Posted by ti...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit a8fba240740dfc096b5d83ad5774bea1ccc8bfc2
Author: tibordigana <ti...@apache.org>
AuthorDate: Mon Jan 13 01:56:13 2020 +0100

    upload artifact
---
 .github/workflows/maven.yml | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index b0f38e0..b1f0eb2 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -39,7 +39,12 @@ jobs:
           java-version: 1.8
 
       - name: Build with Maven
-        run: mvn install -e -B -V -nsu --no-transfer-progress -fn -f surefire-logger-api/pom.xml
+        run: mvn install -e -B -V -nsu --no-transfer-progress -fn -f surefire-logger-api/pom.xml -X
+
+      - uses: actions/upload-artifact@v1
+        with:
+          name: my-artifact
+          path: surefire-logger-api/target
 
       - name: cat dump
         run: cd surefire-logger-api/target/surefire-reports && cat *


[maven-surefire] 04/12: surefire JAR not created. Let's use the old version.

Posted by ti...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit f0c9ae57447331b8175c2211b575c918a9e02ccb
Author: tibordigana <ti...@apache.org>
AuthorDate: Mon Jan 13 02:32:43 2020 +0100

    surefire JAR not created. Let's use the old version.
---
 surefire-logger-api/pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/surefire-logger-api/pom.xml b/surefire-logger-api/pom.xml
index bcac4ad..559e829 100644
--- a/surefire-logger-api/pom.xml
+++ b/surefire-logger-api/pom.xml
@@ -87,7 +87,7 @@
                     <dependency>
                         <groupId>org.apache.maven.surefire</groupId>
                         <artifactId>surefire-shadefire</artifactId>
-<!--                        <version>3.0.0-M4</version>-->
+<!--                        <version>3.0.0-M3</version>-->
                         <version>3.0.0-M3</version> <!-- ${shadedVersion}, but resolved due to https://issues.apache.org/jira/browse/MRELEASE-799 -->
                     </dependency>
                 </dependencies>


[maven-surefire] 01/12: [SUREFIRE-1658] TCP/IP Channel for forked Surefire JVM. Extensions API and SPI. Polymorphism for remote and local process communication.

Posted by ti...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit aaeccca343d56473bdf7acf6c5e380ffb622630e
Author: tibordigana <ti...@apache.org>
AuthorDate: Sat Jul 6 17:51:13 2019 +0200

    [SUREFIRE-1658] TCP/IP Channel for forked Surefire JVM. Extensions API and SPI. Polymorphism for remote and local process communication.
---
 Jenkinsfile                                        |   2 +-
 .../maven/plugin/failsafe/IntegrationTestMojo.java |  10 +
 maven-surefire-common/pom.xml                      |  16 --
 .../plugin/surefire/AbstractSurefireMojo.java      |  23 ++-
 .../maven/plugin/surefire/CommonReflector.java     |  18 +-
 .../AbstractClasspathForkConfiguration.java        |   6 +-
 .../surefire/booterclient/BooterSerializer.java    |   5 +-
 .../booterclient/ClasspathForkConfiguration.java   |   6 +-
 .../booterclient/DefaultForkConfiguration.java     |  13 +-
 .../surefire/booterclient/ForkConfiguration.java   |   2 +
 .../plugin/surefire/booterclient/ForkStarter.java  |  61 +++---
 .../booterclient/JarManifestForkConfiguration.java |   6 +-
 .../ModularClasspathForkConfiguration.java         |   6 +-
 ...InputStream.java => AbstractCommandReader.java} |  16 +-
 ...ommandStream.java => DefaultCommandReader.java} |  60 ++----
 .../DefferedChannelCommandSender.java              |  14 +-
 .../lazytestprovider/TestLessInputStream.java      |   7 +-
 .../lazytestprovider/TestProvidingInputStream.java |  14 +-
 .../output/NativeStdErrStreamConsumer.java         |   6 +-
 .../output/NativeStdOutStreamConsumer.java         |  28 ++-
 .../output/ThreadedStreamConsumer.java             |  12 +-
 .../surefire/extensions/LegacyForkChannel.java     |  31 ++-
 .../surefire/extensions/LegacyForkNodeFactory.java |  19 +-
 .../extensions/NetworkingProcessExecutor.java      | 218 +++++++++++++++++++++
 .../surefire/extensions/PipeProcessExecutor.java   | 144 ++++++++++++++
 .../plugin/surefire/extensions/StdErrAdapter.java  |  25 ++-
 .../plugin/surefire/extensions/StdOutAdapter.java  |  25 ++-
 .../surefire/extensions/SurefireForkChannel.java   |  84 ++++++++
 .../extensions/SurefireForkNodeFactory.java        |  20 +-
 .../AbstractSurefireMojoJava7PlusTest.java         |   1 +
 .../plugin/surefire/AbstractSurefireMojoTest.java  |   1 +
 .../maven/plugin/surefire/CommonReflectorTest.java |  50 +++++
 .../maven/plugin/surefire/MojoMocklessTest.java    |   7 +
 .../plugin/surefire/SurefireReflectorTest.java     |  71 -------
 ...ooterDeserializerProviderConfigurationTest.java |   3 +-
 ...BooterDeserializerStartupConfigurationTest.java |   3 +-
 .../booterclient/DefaultForkConfigurationTest.java |  44 ++---
 .../booterclient/ForkConfigurationTest.java        |  10 +-
 .../ModularClasspathForkConfigurationTest.java     |   2 +-
 .../TestLessInputStreamBuilderTest.java            |  41 +++-
 .../TestProvidingInputStreamTest.java              | 150 ++++++++++----
 .../org/apache/maven/surefire/JUnit4SuiteTest.java |   2 -
 .../maven/plugin/surefire/SurefirePlugin.java      |  10 +
 pom.xml                                            |   6 +-
 .../maven/surefire/booter/BaseProviderFactory.java |  63 +++---
 .../org/apache/maven/surefire/booter/Command.java  |  21 +-
 ...Aware.java => MasterProcessChannelEncoder.java} |  30 ++-
 .../surefire/booter/MasterProcessCommand.java      | 157 +++++----------
 .../surefire/booter/RunOrderParametersAware.java   |  30 ---
 .../surefire/booter/TestArtifactInfoAware.java     |  30 ---
 .../maven/surefire/booter/TestRequestAware.java    |  30 ---
 .../CommandChainReader.java}                       |  19 +-
 .../{booter => providerapi}/CommandListener.java   |   4 +-
 .../providerapi/MasterProcessChannelDecoder.java   |  47 +++++
 .../surefire/providerapi/ProviderParameters.java   |   5 +-
 .../maven/surefire/testset/TestListResolver.java   |   2 +-
 .../maven/surefire/util/ReflectionUtils.java       |  19 --
 .../util/internal/DaemonThreadFactory.java         |  40 +---
 .../java/org/apache/maven/JUnit4SuiteTest.java     |   6 -
 .../surefire/booter/MasterProcessCommandTest.java  | 164 ----------------
 surefire-booter/pom.xml                            |  51 ++++-
 .../maven/surefire/booter/BooterConstants.java     |   1 +
 .../maven/surefire/booter/BooterDeserializer.java  |   5 +
 .../maven/surefire/booter/CommandReader.java       | 113 +++++------
 .../apache/maven/surefire/booter/ForkedBooter.java |  40 +++-
 .../maven/surefire/booter/LazyTestsToRun.java      |  10 +-
 .../surefire/booter/ProviderConfiguration.java     |   2 -
 .../maven/surefire/booter/ProviderFactory.java     |   4 +-
 .../surefire/booter/StartupConfiguration.java      |   5 +
 .../maven/surefire/booter/SurefireReflector.java   |  88 +++------
 .../spi/DefaultMasterProcessChannelDecoder.java    | 162 +++++++++++++++
 .../DefaultMasterProcessChannelDecoderFactory.java |  27 ++-
 ...MasterProcessCommandNoMagicNumberException.java |  17 +-
 .../spi/MasterProcessUnknownCommandException.java  |  18 +-
 ...surefire.spi.MasterProcessChannelDecoderFactory |  19 ++
 .../maven/surefire/booter/CommandReaderTest.java   |  36 ++--
 .../DefaultMasterProcessChannelDecoderTest.java    | 148 ++++++++++++++
 .../java/org/apache/maven/surefire/booter/Foo.java |  35 ++--
 .../surefire/booter/ForkedBooterMockTest.java      |  54 ++++-
 .../surefire/booter/IsolatedClassLoaderTest.java   |  66 +++++++
 .../maven/surefire/booter/JUnit4SuiteTest.java     |   4 +
 .../surefire/booter/NewClassLoaderRunner.java      |   0
 .../surefire/booter/SurefireReflectorTest.java     |  60 +++---
 surefire-extensions-api/pom.xml                    |  38 ++--
 .../maven/surefire/extensions/CommandReader.java   |  23 ++-
 .../maven/surefire/extensions/EventHandler.java    |  13 +-
 .../surefire/extensions/ExecutableCommandline.java |  18 +-
 .../maven/surefire/extensions/ForkChannel.java     |  27 ++-
 .../maven/surefire/extensions/ForkNodeFactory.java |  21 +-
 .../surefire/extensions/StdErrStreamLine.java      |   8 +-
 .../surefire/extensions/StdOutStreamLine.java      |  10 +-
 surefire-extensions-spi/pom.xml                    |  42 ++++
 .../spi/MasterProcessChannelDecoderFactory.java    |  23 ++-
 .../maven/surefire/junit4/JUnit4Provider.java      |   9 +-
 .../maven/surefire/junit4/JUnit4ProviderTest.java  |   2 +-
 .../surefire/junitcore/JUnitCoreProvider.java      |   9 +-
 .../maven/surefire/junitcore/Surefire746Test.java  |   3 +-
 .../maven/surefire/testng/TestNGProvider.java      |   9 +-
 98 files changed, 2038 insertions(+), 1147 deletions(-)

diff --git a/Jenkinsfile b/Jenkinsfile
index c24a81d..ac8c07f 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -35,7 +35,7 @@ final def mavens = env.BRANCH_NAME == 'master' ? ['3.6.x', '3.2.x'] : ['3.6.x']
 // all non-EOL versions and the first EA
 final def jdks = [14, 13, 11, 8, 7]
 
-final def options = ['-e', '-V', '-B', '-nsu', '-P', 'run-its']
+final def options = ['-e', '-V', '-B', '-nsu', '-DskipTests', '-DskipITs']
 final def goals = ['clean', 'install']
 final def goalsDepl = ['clean', 'deploy', 'jacoco:report']
 final Map stages = [:]
diff --git a/maven-failsafe-plugin/src/main/java/org/apache/maven/plugin/failsafe/IntegrationTestMojo.java b/maven-failsafe-plugin/src/main/java/org/apache/maven/plugin/failsafe/IntegrationTestMojo.java
index e465bb2..a6771bc 100644
--- a/maven-failsafe-plugin/src/main/java/org/apache/maven/plugin/failsafe/IntegrationTestMojo.java
+++ b/maven-failsafe-plugin/src/main/java/org/apache/maven/plugin/failsafe/IntegrationTestMojo.java
@@ -27,6 +27,7 @@ import org.apache.maven.plugins.annotations.LifecyclePhase;
 import org.apache.maven.plugins.annotations.Mojo;
 import org.apache.maven.plugins.annotations.Parameter;
 import org.apache.maven.plugins.annotations.ResolutionScope;
+import org.apache.maven.surefire.extensions.ForkNodeFactory;
 import org.apache.maven.surefire.suite.RunResult;
 
 import java.io.File;
@@ -384,6 +385,9 @@ public class IntegrationTestMojo
     @Parameter( property = "failsafe.useModulePath", defaultValue = "true" )
     private boolean useModulePath;
 
+    @Parameter( property = "failsafe.forkNode" )
+    private ForkNodeFactory forkNode;
+
     /**
      * You can selectively exclude individual environment variables by enumerating their keys.
      * <br>
@@ -912,6 +916,12 @@ public class IntegrationTestMojo
     }
 
     @Override
+    protected final ForkNodeFactory getForkNode()
+    {
+        return forkNode;
+    }
+
+    @Override
     protected final String[] getExcludedEnvironmentVariables()
     {
         return excludedEnvironmentVariables == null ? new String[0] : excludedEnvironmentVariables;
diff --git a/maven-surefire-common/pom.xml b/maven-surefire-common/pom.xml
index fefe831..8ab9e98 100644
--- a/maven-surefire-common/pom.xml
+++ b/maven-surefire-common/pom.xml
@@ -120,22 +120,6 @@
     <build>
         <plugins>
             <plugin>
-                <artifactId>maven-dependency-plugin</artifactId>
-                <executions>
-                    <execution>
-                        <id>build-test-classpath</id>
-                        <phase>generate-sources</phase>
-                        <goals>
-                            <goal>build-classpath</goal>
-                        </goals>
-                        <configuration>
-                            <includeScope>test</includeScope>
-                            <outputFile>target/test-classpath/cp.txt</outputFile>
-                        </configuration>
-                    </execution>
-                </executions>
-            </plugin>
-            <plugin>
                 <groupId>org.jacoco</groupId>
                 <artifactId>jacoco-maven-plugin</artifactId>
                 <executions>
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 b8a5297..5f2663b 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
@@ -26,6 +26,7 @@ import org.apache.maven.artifact.handler.ArtifactHandler;
 import org.apache.maven.artifact.repository.ArtifactRepository;
 import org.apache.maven.model.Plugin;
 import org.apache.maven.plugin.surefire.extensions.SurefireConsoleOutputReporter;
+import org.apache.maven.plugin.surefire.extensions.SurefireForkNodeFactory;
 import org.apache.maven.plugin.surefire.extensions.SurefireStatelessReporter;
 import org.apache.maven.plugin.surefire.extensions.SurefireStatelessTestsetInfoReporter;
 import org.apache.maven.plugins.annotations.Component;
@@ -73,6 +74,7 @@ import org.apache.maven.surefire.booter.StartupConfiguration;
 import org.apache.maven.surefire.booter.SurefireBooterForkException;
 import org.apache.maven.surefire.booter.SurefireExecutionException;
 import org.apache.maven.surefire.cli.CommandLineOption;
+import org.apache.maven.surefire.extensions.ForkNodeFactory;
 import org.apache.maven.surefire.providerapi.SurefireProvider;
 import org.apache.maven.surefire.report.ReporterConfiguration;
 import org.apache.maven.surefire.suite.RunResult;
@@ -831,6 +833,8 @@ public abstract class AbstractSurefireMojo
 
     protected abstract String getEnableProcessChecker();
 
+    protected abstract ForkNodeFactory getForkNode();
+
     /**
      * This plugin MOJO artifact.
      *
@@ -2254,6 +2258,14 @@ public abstract class AbstractSurefireMojo
                                               getConsoleLogger() );
     }
 
+    // todo this is in separate method and can be better tested than whole method createForkConfiguration()
+    @Nonnull
+    private ForkNodeFactory getForkNodeFactory()
+    {
+        ForkNodeFactory forkNode = getForkNode();
+        return forkNode == null ? new SurefireForkNodeFactory() : forkNode;
+    }
+
     @Nonnull
     private ForkConfiguration createForkConfiguration( Platform platform )
     {
@@ -2263,6 +2275,8 @@ public abstract class AbstractSurefireMojo
 
         Classpath bootClasspath = getArtifactClasspath( shadeFire != null ? shadeFire : surefireBooterArtifact );
 
+        ForkNodeFactory forkNode = getForkNodeFactory();
+
         if ( canExecuteProviderWithModularPath( platform ) )
         {
             return new ModularClasspathForkConfiguration( bootClasspath,
@@ -2277,7 +2291,8 @@ public abstract class AbstractSurefireMojo
                     getEffectiveForkCount(),
                     reuseForks,
                     platform,
-                    getConsoleLogger() );
+                    getConsoleLogger(),
+                    forkNode );
         }
         else if ( getClassLoaderConfiguration().isManifestOnlyJarRequestedAndUsable() )
         {
@@ -2293,7 +2308,8 @@ public abstract class AbstractSurefireMojo
                     getEffectiveForkCount(),
                     reuseForks,
                     platform,
-                    getConsoleLogger() );
+                    getConsoleLogger(),
+                    forkNode );
         }
         else
         {
@@ -2309,7 +2325,8 @@ public abstract class AbstractSurefireMojo
                     getEffectiveForkCount(),
                     reuseForks,
                     platform,
-                    getConsoleLogger() );
+                    getConsoleLogger(),
+                    forkNode );
         }
     }
 
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/CommonReflector.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/CommonReflector.java
index 835fb89..466148f 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/CommonReflector.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/CommonReflector.java
@@ -23,8 +23,8 @@ import org.apache.maven.plugin.surefire.extensions.SurefireConsoleOutputReporter
 import org.apache.maven.plugin.surefire.extensions.SurefireStatelessReporter;
 import org.apache.maven.plugin.surefire.extensions.SurefireStatelessTestsetInfoReporter;
 import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
+import org.apache.maven.plugin.surefire.log.api.ConsoleLoggerDecorator;
 import org.apache.maven.plugin.surefire.report.DefaultReporterFactory;
-import org.apache.maven.surefire.booter.SurefireReflector;
 import org.apache.maven.surefire.util.SurefireReflectionException;
 
 import javax.annotation.Nonnull;
@@ -71,7 +71,7 @@ public class CommonReflector
     {
         Class<?>[] args = { this.startupReportConfiguration, this.consoleLogger };
         Object src = createStartupReportConfiguration( startupReportConfiguration );
-        Object logger = SurefireReflector.createConsoleLogger( consoleLogger, surefireClassLoader );
+        Object logger = createConsoleLogger( consoleLogger, surefireClassLoader );
         Object[] params = { src, logger };
         return instantiateObject( DefaultReporterFactory.class.getName(), args, params, surefireClassLoader );
     }
@@ -84,7 +84,6 @@ public class CommonReflector
                                                      int.class, String.class, String.class, boolean.class,
                                                      statelessTestsetReporter, consoleOutputReporter,
                                                      statelessTestsetInfoReporter );
-        //noinspection BooleanConstructorCall
         Object[] params = { reporterConfiguration.isUseFile(), reporterConfiguration.isPrintSummary(),
             reporterConfiguration.getReportFormat(), reporterConfiguration.isRedirectTestOutputToFile(),
             reporterConfiguration.getReportsDirectory(),
@@ -98,4 +97,17 @@ public class CommonReflector
         };
         return newInstance( constructor, params );
     }
+
+    static Object createConsoleLogger( ConsoleLogger consoleLogger, ClassLoader cl )
+    {
+        try
+        {
+            Class<?> decoratorClass = cl.loadClass( ConsoleLoggerDecorator.class.getName() );
+            return getConstructor( decoratorClass, Object.class ).newInstance( consoleLogger );
+        }
+        catch ( Exception e )
+        {
+            throw new SurefireReflectionException( e );
+        }
+    }
 }
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/AbstractClasspathForkConfiguration.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/AbstractClasspathForkConfiguration.java
index 692f486..08a8373 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/AbstractClasspathForkConfiguration.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/AbstractClasspathForkConfiguration.java
@@ -21,6 +21,7 @@ package org.apache.maven.plugin.surefire.booterclient;
 
 import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
 import org.apache.maven.surefire.booter.Classpath;
+import org.apache.maven.surefire.extensions.ForkNodeFactory;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
@@ -49,10 +50,11 @@ abstract class AbstractClasspathForkConfiguration
                                         int forkCount,
                                         boolean reuseForks,
                                         @Nonnull Platform pluginPlatform,
-                                        @Nonnull ConsoleLogger log )
+                                        @Nonnull ConsoleLogger log,
+                                        @Nonnull ForkNodeFactory forkNodeFactory )
     {
         super( bootClasspath, tempDirectory, debugLine, workingDirectory, modelProperties, argLine,
-                environmentVariables, excludedEnvironmentVariables, debug, forkCount, reuseForks, pluginPlatform, log );
+                environmentVariables, excludedEnvironmentVariables, debug, forkCount, reuseForks, pluginPlatform, log, forkNodeFactory );
     }
 
     @Override
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 7eacb74..b166cc2 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
@@ -71,6 +71,7 @@ import static org.apache.maven.surefire.booter.BooterConstants.TESTARTIFACT_CLAS
 import static org.apache.maven.surefire.booter.BooterConstants.TESTARTIFACT_VERSION;
 import static org.apache.maven.surefire.booter.BooterConstants.USEMANIFESTONLYJAR;
 import static org.apache.maven.surefire.booter.BooterConstants.USESYSTEMCLASSLOADER;
+import static org.apache.maven.surefire.booter.BooterConstants.FORK_NODE_CONNECTION_STRING;
 import static org.apache.maven.surefire.booter.SystemPropertyManager.writePropertiesFile;
 
 /**
@@ -102,11 +103,11 @@ class BooterSerializer
      */
     File serialize( KeyValueSource sourceProperties, ProviderConfiguration providerConfiguration,
                     StartupConfiguration startupConfiguration, Object testSet, boolean readTestsFromInStream,
-                    Long pid, int forkNumber )
+                    Long pid, int forkNumber, String forkNodeConnectionString )
         throws IOException
     {
         SurefireProperties properties = new SurefireProperties( sourceProperties );
-
+        properties.setProperty( FORK_NODE_CONNECTION_STRING, forkNodeConnectionString );
         properties.setProperty( PLUGIN_PID, pid );
 
         AbstractPathConfiguration cp = startupConfiguration.getClasspathConfiguration();
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ClasspathForkConfiguration.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ClasspathForkConfiguration.java
index 1ca3932..94b7ee3 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ClasspathForkConfiguration.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ClasspathForkConfiguration.java
@@ -24,6 +24,7 @@ import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
 import org.apache.maven.surefire.booter.Classpath;
 import org.apache.maven.surefire.booter.StartupConfiguration;
 import org.apache.maven.surefire.booter.SurefireBooterForkException;
+import org.apache.maven.surefire.extensions.ForkNodeFactory;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
@@ -48,10 +49,11 @@ public final class ClasspathForkConfiguration
                                        @Nonnull String[] excludedEnvironmentVariables,
                                        boolean debug, int forkCount,
                                        boolean reuseForks, @Nonnull Platform pluginPlatform,
-                                       @Nonnull ConsoleLogger log )
+                                       @Nonnull ConsoleLogger log,
+                                       @Nonnull ForkNodeFactory forkNodeFactory )
     {
         super( bootClasspath, tempDirectory, debugLine, workingDirectory, modelProperties, argLine,
-                environmentVariables, excludedEnvironmentVariables, debug, forkCount, reuseForks, pluginPlatform, log );
+                environmentVariables, excludedEnvironmentVariables, debug, forkCount, reuseForks, pluginPlatform, log, forkNodeFactory );
     }
 
     @Override
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/DefaultForkConfiguration.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/DefaultForkConfiguration.java
index 4ab4435..443bf45 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/DefaultForkConfiguration.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/DefaultForkConfiguration.java
@@ -26,6 +26,7 @@ import org.apache.maven.surefire.booter.AbstractPathConfiguration;
 import org.apache.maven.surefire.booter.Classpath;
 import org.apache.maven.surefire.booter.StartupConfiguration;
 import org.apache.maven.surefire.booter.SurefireBooterForkException;
+import org.apache.maven.surefire.extensions.ForkNodeFactory;
 import org.apache.maven.surefire.util.internal.ImmutableMap;
 
 import javax.annotation.Nonnull;
@@ -65,6 +66,7 @@ public abstract class DefaultForkConfiguration
     private final boolean reuseForks;
     @Nonnull private final Platform pluginPlatform;
     @Nonnull private final ConsoleLogger log;
+    @Nonnull private final ForkNodeFactory forkNodeFactory;
 
     @SuppressWarnings( "checkstyle:parameternumber" )
     protected DefaultForkConfiguration( @Nonnull Classpath booterClasspath,
@@ -79,7 +81,8 @@ public abstract class DefaultForkConfiguration
                                      int forkCount,
                                      boolean reuseForks,
                                      @Nonnull Platform pluginPlatform,
-                                     @Nonnull ConsoleLogger log )
+                                     @Nonnull ConsoleLogger log,
+                                     @Nonnull ForkNodeFactory forkNodeFactory )
     {
         this.booterClasspath = booterClasspath;
         this.tempDirectory = tempDirectory;
@@ -94,6 +97,7 @@ public abstract class DefaultForkConfiguration
         this.reuseForks = reuseForks;
         this.pluginPlatform = pluginPlatform;
         this.log = log;
+        this.forkNodeFactory = forkNodeFactory;
     }
 
     protected abstract void resolveClasspath( @Nonnull OutputStreamFlushableCommandline cli,
@@ -108,6 +112,13 @@ public abstract class DefaultForkConfiguration
         return jvmArgLine;
     }
 
+    @Nonnull
+    @Override
+    public final ForkNodeFactory getForkNodeFactory()
+    {
+        return forkNodeFactory;
+    }
+
     /**
      * @param config       The startup configuration
      * @param forkNumber   index of forked JVM, to be the replacement in the argLine
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkConfiguration.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkConfiguration.java
index 92bebd0..9fddf96 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkConfiguration.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkConfiguration.java
@@ -25,6 +25,7 @@ import org.apache.maven.surefire.booter.Classpath;
 import org.apache.maven.surefire.booter.ForkedBooter;
 import org.apache.maven.surefire.booter.StartupConfiguration;
 import org.apache.maven.surefire.booter.SurefireBooterForkException;
+import org.apache.maven.surefire.extensions.ForkNodeFactory;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
@@ -39,6 +40,7 @@ public abstract class ForkConfiguration
 {
     static final String DEFAULT_PROVIDER_CLASS = ForkedBooter.class.getName();
 
+    @Nonnull public abstract ForkNodeFactory getForkNodeFactory();
     @Nonnull public abstract File getTempDirectory();
     @Nullable protected abstract String getDebugLine();
     @Nonnull protected abstract File getWorkingDirectory();
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java
index 0ba2f46..73340a8 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java
@@ -22,7 +22,7 @@ package org.apache.maven.plugin.surefire.booterclient;
 import org.apache.maven.plugin.surefire.CommonReflector;
 import org.apache.maven.plugin.surefire.StartupReportConfiguration;
 import org.apache.maven.plugin.surefire.SurefireProperties;
-import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.AbstractForkInputStream;
+import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.AbstractCommandReader;
 import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.NotifiableTestStream;
 import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.OutputStreamFlushableCommandline;
 import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.TestLessInputStream;
@@ -38,7 +38,6 @@ import org.apache.maven.surefire.extensions.util.CommandlineStreams;
 import org.apache.maven.surefire.extensions.util.CountdownCloseable;
 import org.apache.maven.surefire.extensions.util.LineConsumerThread;
 import org.apache.maven.surefire.extensions.util.StreamFeeder;
-import org.apache.maven.surefire.shared.utils.cli.CommandLineException;
 import org.apache.maven.surefire.booter.AbstractPathConfiguration;
 import org.apache.maven.surefire.booter.PropertiesWrapper;
 import org.apache.maven.surefire.booter.ProviderConfiguration;
@@ -47,6 +46,8 @@ import org.apache.maven.surefire.booter.Shutdown;
 import org.apache.maven.surefire.booter.StartupConfiguration;
 import org.apache.maven.surefire.booter.SurefireBooterForkException;
 import org.apache.maven.surefire.booter.SurefireExecutionException;
+import org.apache.maven.surefire.extensions.ForkChannel;
+import org.apache.maven.surefire.extensions.ForkNodeFactory;
 import org.apache.maven.surefire.providerapi.SurefireProvider;
 import org.apache.maven.surefire.report.StackTraceWriter;
 import org.apache.maven.surefire.suite.RunResult;
@@ -79,7 +80,6 @@ import static java.lang.StrictMath.min;
 import static java.lang.System.currentTimeMillis;
 import static java.lang.Thread.currentThread;
 import static java.util.Collections.addAll;
-import static java.util.Objects.requireNonNull;
 import static java.util.concurrent.Executors.newScheduledThreadPool;
 import static java.util.concurrent.TimeUnit.MILLISECONDS;
 import static java.util.concurrent.TimeUnit.SECONDS;
@@ -192,7 +192,7 @@ public class ForkStarter
                 {
                     closeable.close();
                 }
-                catch ( IOException e )
+                catch ( IOException | RuntimeException e )
                 {
                     // This error does not fail a test and does not necessarily mean that the forked JVM std/out stream
                     // was not closed, see ThreadedStreamConsumer. This error means that JVM wrote messages to a native
@@ -212,11 +212,17 @@ public class ForkStarter
         @Override
         public void close()
         {
-            run();
-            testProvidingInputStream.clear();
-            if ( inputStreamCloserHook != null )
+            try
+            {
+                run();
+            }
+            finally
             {
-                removeShutdownHook( inputStreamCloserHook );
+                testProvidingInputStream.clear();
+                if ( inputStreamCloserHook != null )
+                {
+                    removeShutdownHook( inputStreamCloserHook );
+                }
             }
         }
 
@@ -289,7 +295,8 @@ public class ForkStarter
             defaultReporterFactories.add( forkedReporterFactory );
             ForkClient forkClient =
                     new ForkClient( forkedReporterFactory, stream, log, new AtomicBoolean(), forkNumber );
-            return fork( null, props, forkClient, effectiveSystemProperties, forkNumber, stream, false );
+            return fork( null, props, forkClient, effectiveSystemProperties, forkNumber, stream,
+                    forkConfiguration.getForkNodeFactory(), false );
         }
         finally
         {
@@ -379,7 +386,8 @@ public class ForkStarter
                         try
                         {
                             return fork( null, new PropertiesWrapper( providerProperties ), forkClient,
-                                    effectiveSystemProperties, forkNumber, testProvidingInputStream, true );
+                                    effectiveSystemProperties, forkNumber, testProvidingInputStream,
+                                    forkConfiguration.getForkNodeFactory(), true );
                         }
                         finally
                         {
@@ -453,7 +461,8 @@ public class ForkStarter
                         {
                             return fork( testSet,
                                          new PropertiesWrapper( providerConfiguration.getProviderProperties() ),
-                                         forkClient, effectiveSystemProperties, forkNumber, stream, false );
+                                         forkClient, effectiveSystemProperties, forkNumber, stream,
+                                         forkConfiguration.getForkNodeFactory(), false );
                         }
                         finally
                         {
@@ -549,21 +558,25 @@ public class ForkStarter
 
     private RunResult fork( Object testSet, PropertiesWrapper providerProperties, ForkClient forkClient,
                             SurefireProperties effectiveSystemProperties, int forkNumber,
-                            AbstractForkInputStream commandInputStream, boolean readTestsFromInStream )
+                            AbstractCommandReader commandReader, ForkNodeFactory forkNodeFactory,
+                            boolean readTestsFromInStream )
         throws SurefireBooterForkException
     {
         final String tempDir;
         final File surefireProperties;
         final File systPropsFile;
+        final ForkChannel forkChannel;
         try
         {
+            forkChannel = forkNodeFactory.createForkChannel();
             tempDir = forkConfiguration.getTempDirectory().getCanonicalPath();
             BooterSerializer booterSerializer = new BooterSerializer( forkConfiguration );
             Long pluginPid = forkConfiguration.getPluginPlatform().getPluginPid();
-            surefireProperties = booterSerializer.serialize( providerProperties, providerConfiguration,
-                    startupConfiguration, testSet, readTestsFromInStream, pluginPid, forkNumber );
-
             log.debug( "Determined Maven Process ID " + pluginPid );
+            String connectionString = forkChannel.getForkNodeConnectionString();
+            log.debug( "Fork Channel [" + forkNumber + "] connection string " + connectionString );
+            surefireProperties = booterSerializer.serialize( providerProperties, providerConfiguration,
+                    startupConfiguration, testSet, readTestsFromInStream, pluginPid, forkNumber, connectionString );
 
             if ( effectiveSystemProperties != null )
             {
@@ -588,10 +601,7 @@ public class ForkStarter
         OutputStreamFlushableCommandline cli =
                 forkConfiguration.createCommandLine( startupConfiguration, forkNumber, dumpLogDir );
 
-        if ( commandInputStream != null )
-        {
-            commandInputStream.setFlushReceiverProvider( cli );
-        }
+        commandReader.setFlushReceiverProvider( cli );
 
         cli.createArg().setValue( tempDir );
         cli.createArg().setValue( DUMP_FILE_PREFIX + forkNumber );
@@ -602,7 +612,7 @@ public class ForkStarter
         }
 
         ThreadedStreamConsumer eventConsumer = new ThreadedStreamConsumer( forkClient );
-        CloseableCloser closer = new CloseableCloser( forkNumber, eventConsumer, requireNonNull( commandInputStream ) );
+        CloseableCloser closer = new CloseableCloser( forkNumber, eventConsumer, commandReader );
 
         log.debug( "Forking command line: " + cli );
 
@@ -615,6 +625,8 @@ public class ForkStarter
         DefaultReporterFactory reporter = forkClient.getDefaultReporterFactory();
         currentForkClients.add( forkClient );
         CountdownCloseable countdownCloseable = new CountdownCloseable( eventConsumer, 2 );
+        //todo implement extension: forkChannel.createExecutableCommandline()
+        //todo implement extension: executableCommandline.executeCommandLineAsCallable(...)
         try ( CommandlineExecutor exec = new CommandlineExecutor( cli, countdownCloseable ) )
         {
             // default impl of the extension - solves everything including the encoder/decoder, Process starter,
@@ -623,7 +635,7 @@ public class ForkStarter
             // BEGIN: beginning of the call of the extension
             CommandlineStreams streams = exec.execute();
             closer.addCloseable( streams );
-            in = new StreamFeeder( "std-in-fork-" + forkNumber, streams.getStdInChannel(), commandInputStream );
+            in = new StreamFeeder( "std-in-fork-" + forkNumber, streams.getStdInChannel(), commandReader );
             in.start();
             out = new LineConsumerThread( "std-out-fork-" + forkNumber, streams.getStdOutChannel(),
                                           eventConsumer, countdownCloseable );
@@ -652,8 +664,9 @@ public class ForkStarter
             out.disable();
             err.disable();
         }
-        catch ( CommandLineException e )
+        catch ( Exception e )
         {
+            // CommandLineException from pipes and IOException from sockets
             runResult = failure( reporter.getGlobalRunStatistics().getRunResult(), e );
             String cliErr = e.getLocalizedMessage();
             Throwable cause = e.getCause();
@@ -665,7 +678,7 @@ public class ForkStarter
             currentForkClients.remove( forkClient );
             try
             {
-                Closeable c = forkClient.isSaidGoodBye() ? closer : commandInputStream;
+                Closeable c = forkClient.isSaidGoodBye() ? closer : commandReader;
                 c.close();
             }
             catch ( IOException e )
@@ -710,7 +723,7 @@ public class ForkStarter
                     //noinspection ThrowFromFinallyBlock
                     throw new SurefireBooterForkException( "There was an error in the forked process"
                                                         + detail
-                                                        + ( stackTrace == null ? "" : stackTrace ), cause );
+                                                        + ( stackTrace == null ? "" : "\n" + stackTrace ), cause );
                 }
                 if ( !forkClient.isSaidGoodBye() )
                 {
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/JarManifestForkConfiguration.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/JarManifestForkConfiguration.java
index 02c275c..281118b 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/JarManifestForkConfiguration.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/JarManifestForkConfiguration.java
@@ -28,6 +28,7 @@ import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
 import org.apache.maven.surefire.booter.Classpath;
 import org.apache.maven.surefire.booter.StartupConfiguration;
 import org.apache.maven.surefire.booter.SurefireBooterForkException;
+import org.apache.maven.surefire.extensions.ForkNodeFactory;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
@@ -70,10 +71,11 @@ public final class JarManifestForkConfiguration
                                          @Nonnull String[] excludedEnvironmentVariables,
                                          boolean debug,
                                          int forkCount, boolean reuseForks, @Nonnull Platform pluginPlatform,
-                                         @Nonnull ConsoleLogger log )
+                                         @Nonnull ConsoleLogger log,
+                                         @Nonnull ForkNodeFactory forkNodeFactory )
     {
         super( bootClasspath, tempDirectory, debugLine, workingDirectory, modelProperties, argLine,
-                environmentVariables, excludedEnvironmentVariables, debug, forkCount, reuseForks, pluginPlatform, log );
+                environmentVariables, excludedEnvironmentVariables, debug, forkCount, reuseForks, pluginPlatform, log, forkNodeFactory );
     }
 
     @Override
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ModularClasspathForkConfiguration.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ModularClasspathForkConfiguration.java
index 8f5030b..67a977c 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ModularClasspathForkConfiguration.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ModularClasspathForkConfiguration.java
@@ -28,6 +28,7 @@ import org.apache.maven.surefire.booter.ModularClasspath;
 import org.apache.maven.surefire.booter.ModularClasspathConfiguration;
 import org.apache.maven.surefire.booter.StartupConfiguration;
 import org.apache.maven.surefire.booter.SurefireBooterForkException;
+import org.apache.maven.surefire.extensions.ForkNodeFactory;
 
 import javax.annotation.Nonnegative;
 import javax.annotation.Nonnull;
@@ -67,10 +68,11 @@ public class ModularClasspathForkConfiguration
                                               @Nonnegative int forkCount,
                                               boolean reuseForks,
                                               @Nonnull Platform pluginPlatform,
-                                              @Nonnull ConsoleLogger log )
+                                              @Nonnull ConsoleLogger log,
+                                              @Nonnull ForkNodeFactory forkNodeFactory )
     {
         super( bootClasspath, tempDirectory, debugLine, workingDirectory, modelProperties, argLine,
-                environmentVariables, excludedEnvironmentVariables, debug, forkCount, reuseForks, pluginPlatform, log );
+                environmentVariables, excludedEnvironmentVariables, debug, forkCount, reuseForks, pluginPlatform, log, forkNodeFactory );
     }
 
     @Override
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/AbstractForkInputStream.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/AbstractCommandReader.java
similarity index 84%
rename from maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/AbstractForkInputStream.java
rename to maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/AbstractCommandReader.java
index a884c15..a31e9f7 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/AbstractForkInputStream.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/AbstractCommandReader.java
@@ -19,32 +19,34 @@ package org.apache.maven.plugin.surefire.booterclient.lazytestprovider;
  * under the License.
  */
 
+import org.apache.maven.surefire.extensions.CommandReader;
+
 import java.io.IOException;
-import java.io.InputStream;
 
 import static java.util.Objects.requireNonNull;
 
 /**
- * Reader stream sends bytes to forked jvm std-{@link InputStream input-stream}.
+ * Stream reader returns bytes which ar finally sent to the forked jvm std-input-stream.
  *
  * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
  * @since 2.19
  */
-public abstract class AbstractForkInputStream
-    extends InputStream
-    implements NotifiableTestStream
+public abstract class AbstractCommandReader
+        implements CommandReader, DefferedChannelCommandSender
 {
     private volatile FlushReceiverProvider flushReceiverProvider;
 
     /**
      * @param flushReceiverProvider the provider for a flush receiver.
      */
+    @Override
     public void setFlushReceiverProvider( FlushReceiverProvider flushReceiverProvider )
     {
         this.flushReceiverProvider = requireNonNull( flushReceiverProvider );
     }
 
-    protected boolean tryFlush()
+    @Override
+    public void tryFlush()
         throws IOException
     {
         if ( flushReceiverProvider != null )
@@ -53,9 +55,7 @@ public abstract class AbstractForkInputStream
             if ( flushReceiver != null )
             {
                 flushReceiver.flush();
-                return true;
             }
         }
-        return false;
     }
 }
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/AbstractCommandStream.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/DefaultCommandReader.java
similarity index 63%
rename from maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/AbstractCommandStream.java
rename to maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/DefaultCommandReader.java
index 31b56c4..9aa19c3 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/AbstractCommandStream.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/DefaultCommandReader.java
@@ -20,7 +20,6 @@ package org.apache.maven.plugin.surefire.booterclient.lazytestprovider;
  */
 
 import org.apache.maven.surefire.booter.Command;
-import org.apache.maven.surefire.booter.MasterProcessCommand;
 
 import java.io.IOException;
 
@@ -31,14 +30,9 @@ import java.io.IOException;
  * @since 2.19
  * @see org.apache.maven.surefire.booter.Command
  */
-public abstract class AbstractCommandStream
-    extends AbstractForkInputStream
+public abstract class DefaultCommandReader
+        extends AbstractCommandReader
 {
-    private byte[] currentBuffer;
-    private int currentPos;
-
-    protected abstract boolean isClosed();
-
     /**
      * Opposite to {@link #isClosed()}.
      * @return {@code true} if not closed
@@ -62,59 +56,35 @@ public abstract class AbstractCommandStream
     protected abstract Command nextCommand();
 
     /**
-     * Returns quietly and immediately.
-     */
-    protected final void invalidateInternalBuffer()
-    {
-        currentBuffer = null;
-        currentPos = 0;
-    }
-
-    /**
      * Used by single thread in StreamFeeder class.
      *
      * @return {@inheritDoc}
      * @throws IOException {@inheritDoc}
      */
-    @SuppressWarnings( "checkstyle:magicnumber" )
     @Override
-    public int read()
+    public Command readNextCommand()
         throws IOException
     {
+        tryFlush();
+
         if ( isClosed() )
         {
-            tryFlush();
-            return -1;
+            return null;
         }
 
-        if ( currentBuffer == null )
+        if ( !canContinue() )
         {
-            tryFlush();
-
-            if ( !canContinue() )
-            {
-                close();
-                return -1;
-            }
-
-            beforeNextCommand();
-
-            if ( isClosed() )
-            {
-                return -1;
-            }
-
-            Command cmd = nextCommand();
-            MasterProcessCommand cmdType = cmd.getCommandType();
-            currentBuffer = cmdType.hasDataType() ? cmdType.encode( cmd.getData() ) : cmdType.encode();
+            close();
+            return null;
         }
 
-        int b =  currentBuffer[currentPos++] & 0xff;
-        if ( currentPos == currentBuffer.length )
+        beforeNextCommand();
+
+        if ( isClosed() )
         {
-            currentBuffer = null;
-            currentPos = 0;
+            return null;
         }
-        return b;
+
+        return nextCommand();
     }
 }
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/ShutdownAware.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/DefferedChannelCommandSender.java
similarity index 66%
rename from surefire-api/src/main/java/org/apache/maven/surefire/booter/ShutdownAware.java
rename to maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/DefferedChannelCommandSender.java
index 0bfcdb8..e489caa 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/ShutdownAware.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/DefferedChannelCommandSender.java
@@ -1,4 +1,4 @@
-package org.apache.maven.surefire.booter;
+package org.apache.maven.plugin.surefire.booterclient.lazytestprovider;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -19,13 +19,17 @@ package org.apache.maven.surefire.booter;
  * under the License.
  */
 
+import java.io.Closeable;
+
 /**
- * See the plugin configuration parameter {@code shutdown}.
+ * Physical implementation of command sender.<br>
+ * Instance of {@link AbstractCommandReader} (namely {@link TestLessInputStream} or {@link TestProvidingInputStream}).
  *
  * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
- * @since 2.19
+ * @since 3.0.0-M4
  */
-public interface ShutdownAware
+public interface DefferedChannelCommandSender
+    extends NotifiableTestStream, Closeable
 {
-    void setShutdown( Shutdown shutdown );
+    void setFlushReceiverProvider( FlushReceiverProvider flushReceiverProvider );
 }
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestLessInputStream.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestLessInputStream.java
index 372ce00..a1060a7 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestLessInputStream.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestLessInputStream.java
@@ -45,7 +45,7 @@ import static org.apache.maven.surefire.booter.Command.toShutdown;
  * @since 2.19
  */
 public final class TestLessInputStream
-    extends AbstractCommandStream
+        extends DefaultCommandReader
 {
     private final Semaphore barrier = new Semaphore( 0 );
 
@@ -108,7 +108,7 @@ public final class TestLessInputStream
     }
 
     @Override
-    protected boolean isClosed()
+    public boolean isClosed()
     {
         return closed.get();
     }
@@ -141,7 +141,6 @@ public final class TestLessInputStream
     {
         if ( closed.compareAndSet( false, true ) )
         {
-            invalidateInternalBuffer();
             barrier.drainPermits();
             barrier.release();
         }
@@ -166,8 +165,6 @@ public final class TestLessInputStream
         }
         catch ( InterruptedException e )
         {
-            // help GC to free this object because StreamFeeder Thread cannot read it anyway after IOE
-            invalidateInternalBuffer();
             throw new IOException( e.getLocalizedMessage() );
         }
     }
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStream.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStream.java
index a4255cc..6f3a4de 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStream.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStream.java
@@ -31,6 +31,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
 import static org.apache.maven.surefire.booter.Command.BYE_ACK;
 import static org.apache.maven.surefire.booter.Command.NOOP;
 import static org.apache.maven.surefire.booter.Command.SKIP_SINCE_NEXT_TEST;
+import static org.apache.maven.surefire.booter.Command.TEST_SET_FINISHED;
 import static org.apache.maven.surefire.booter.Command.toRunClass;
 import static org.apache.maven.surefire.booter.Command.toShutdown;
 
@@ -50,7 +51,7 @@ import static org.apache.maven.surefire.booter.Command.toShutdown;
  * @author Tibor Digana (tibor17)
  */
 public final class TestProvidingInputStream
-    extends AbstractCommandStream
+        extends DefaultCommandReader
 {
     private final Semaphore barrier = new Semaphore( 0 );
 
@@ -77,7 +78,7 @@ public final class TestProvidingInputStream
     {
         if ( canContinue() )
         {
-            commands.add( Command.TEST_SET_FINISHED );
+            commands.add( TEST_SET_FINISHED );
             barrier.release();
         }
     }
@@ -129,7 +130,7 @@ public final class TestProvidingInputStream
         if ( cmd == null )
         {
             String cmdData = testClassNames.poll();
-            return cmdData == null ? Command.TEST_SET_FINISHED : toRunClass( cmdData );
+            return cmdData == null ? TEST_SET_FINISHED : toRunClass( cmdData );
         }
         else
         {
@@ -145,7 +146,7 @@ public final class TestProvidingInputStream
     }
 
     @Override
-    protected boolean isClosed()
+    public boolean isClosed()
     {
         return closed.get();
     }
@@ -167,7 +168,6 @@ public final class TestProvidingInputStream
     {
         if ( closed.compareAndSet( false, true ) )
         {
-            invalidateInternalBuffer();
             barrier.drainPermits();
             barrier.release();
         }
@@ -182,9 +182,7 @@ public final class TestProvidingInputStream
         }
         catch ( InterruptedException e )
         {
-            // help GC to free this object because StreamFeeder Thread cannot read it anyway after IOE
-            invalidateInternalBuffer();
-            throw new IOException( e.getLocalizedMessage() );
+            throw new IOException( e.getLocalizedMessage(), e );
         }
     }
 }
\ No newline at end of file
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/NativeStdErrStreamConsumer.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/NativeStdErrStreamConsumer.java
index b17bfe4..6082096 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/NativeStdErrStreamConsumer.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/NativeStdErrStreamConsumer.java
@@ -20,7 +20,7 @@ package org.apache.maven.plugin.surefire.booterclient.output;
  */
 
 import org.apache.maven.plugin.surefire.report.DefaultReporterFactory;
-import org.apache.maven.surefire.shared.utils.cli.StreamConsumer;
+import org.apache.maven.surefire.extensions.StdErrStreamLine;
 
 /**
  * Used by forked JMV, see {@link org.apache.maven.plugin.surefire.booterclient.ForkStarter}.
@@ -30,7 +30,7 @@ import org.apache.maven.surefire.shared.utils.cli.StreamConsumer;
  * @see org.apache.maven.plugin.surefire.booterclient.ForkStarter
  */
 public final class NativeStdErrStreamConsumer
-    implements StreamConsumer
+    implements StdErrStreamLine
 {
     private final DefaultReporterFactory defaultReporterFactory;
 
@@ -40,7 +40,7 @@ public final class NativeStdErrStreamConsumer
     }
 
     @Override
-    public void consumeLine( String line )
+    public void handleLine( String line )
     {
         InPluginProcessDumpSingleton.getSingleton()
                 .dumpStreamText( line, defaultReporterFactory.getReportsDirectory() );
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/MainCliOptionsAware.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/NativeStdOutStreamConsumer.java
similarity index 58%
copy from surefire-api/src/main/java/org/apache/maven/surefire/booter/MainCliOptionsAware.java
copy to maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/NativeStdOutStreamConsumer.java
index eddebed..1f915ae 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/MainCliOptionsAware.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/NativeStdOutStreamConsumer.java
@@ -1,4 +1,4 @@
-package org.apache.maven.surefire.booter;
+package org.apache.maven.plugin.surefire.booterclient.output;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -9,7 +9,7 @@ package org.apache.maven.surefire.booter;
  * "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
+ *   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
@@ -19,17 +19,25 @@ package org.apache.maven.surefire.booter;
  * under the License.
  */
 
-import org.apache.maven.surefire.cli.CommandLineOption;
-
-import java.util.List;
+import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
+import org.apache.maven.surefire.extensions.StdOutStreamLine;
 
 /**
- * CLI options in plugin (main) JVM process.
  *
- * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
- * @since 2.19
  */
-interface MainCliOptionsAware
+public class NativeStdOutStreamConsumer
+        implements StdOutStreamLine
 {
-    void setMainCliOptions( List<CommandLineOption> mainCliOptions );
+    private final ConsoleLogger logger;
+
+    public NativeStdOutStreamConsumer( ConsoleLogger logger )
+    {
+        this.logger = logger;
+    }
+
+    @Override
+    public void handleLine( String line )
+    {
+        logger.info( line );
+    }
 }
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ThreadedStreamConsumer.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ThreadedStreamConsumer.java
index 853d35c..b3c5c29 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ThreadedStreamConsumer.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ThreadedStreamConsumer.java
@@ -20,8 +20,10 @@ package org.apache.maven.plugin.surefire.booterclient.output;
  */
 
 import org.apache.maven.surefire.shared.utils.cli.StreamConsumer;
+import org.apache.maven.surefire.extensions.EventHandler;
 import org.apache.maven.surefire.util.internal.DaemonThreadFactory;
 
+import javax.annotation.Nonnull;
 import java.io.Closeable;
 import java.io.IOException;
 import java.util.concurrent.ArrayBlockingQueue;
@@ -36,7 +38,7 @@ import static java.lang.Thread.currentThread;
  * @author Kristian Rosenvold
  */
 public final class ThreadedStreamConsumer
-        implements StreamConsumer, Closeable
+        implements EventHandler, StreamConsumer, Closeable
 {
     private static final String END_ITEM = "";
 
@@ -113,7 +115,13 @@ public final class ThreadedStreamConsumer
     }
 
     @Override
-    public void consumeLine( String s )
+    public void handleEvent( @Nonnull String event )
+    {
+        consumeLine( event );
+    }
+
+    @Override
+    public void consumeLine( @Nonnull String s )
     {
         if ( stop.get() )
         {
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/MainCliOptionsAware.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/LegacyForkChannel.java
similarity index 57%
copy from surefire-api/src/main/java/org/apache/maven/surefire/booter/MainCliOptionsAware.java
copy to maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/LegacyForkChannel.java
index eddebed..28cad5a 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/MainCliOptionsAware.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/LegacyForkChannel.java
@@ -1,4 +1,4 @@
-package org.apache.maven.surefire.booter;
+package org.apache.maven.plugin.surefire.extensions;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -19,17 +19,32 @@ package org.apache.maven.surefire.booter;
  * under the License.
  */
 
-import org.apache.maven.surefire.cli.CommandLineOption;
+import org.apache.maven.surefire.extensions.ExecutableCommandline;
+import org.apache.maven.surefire.extensions.ForkChannel;
 
-import java.util.List;
+import javax.annotation.Nonnull;
+import java.io.IOException;
 
 /**
- * CLI options in plugin (main) JVM process.
  *
- * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
- * @since 2.19
  */
-interface MainCliOptionsAware
+final class LegacyForkChannel implements ForkChannel
 {
-    void setMainCliOptions( List<CommandLineOption> mainCliOptions );
+    @Override
+    public String getForkNodeConnectionString()
+    {
+        return "pipe://";
+    }
+
+    @Nonnull
+    @Override
+    public ExecutableCommandline createExecutableCommandline() throws IOException
+    {
+        return new PipeProcessExecutor();
+    }
+
+    @Override
+    public void close() throws IOException
+    {
+    }
 }
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/DirectoryScannerParametersAware.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/LegacyForkNodeFactory.java
similarity index 65%
copy from surefire-api/src/main/java/org/apache/maven/surefire/booter/DirectoryScannerParametersAware.java
copy to maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/LegacyForkNodeFactory.java
index cefeb33..7231d00 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/DirectoryScannerParametersAware.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/LegacyForkNodeFactory.java
@@ -1,4 +1,4 @@
-package org.apache.maven.surefire.booter;
+package org.apache.maven.plugin.surefire.extensions;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -19,12 +19,21 @@ package org.apache.maven.surefire.booter;
  * under the License.
  */
 
-import org.apache.maven.surefire.testset.DirectoryScannerParameters;
+import org.apache.maven.surefire.extensions.ForkChannel;
+import org.apache.maven.surefire.extensions.ForkNodeFactory;
+
+import javax.annotation.Nonnull;
+import java.io.IOException;
 
 /**
- * @author Kristian Rosenvold
+ *
  */
-interface DirectoryScannerParametersAware
+public class LegacyForkNodeFactory implements ForkNodeFactory
 {
-    void setDirectoryScannerParameters( DirectoryScannerParameters directoryScanner );
+    @Nonnull
+    @Override
+    public ForkChannel createForkChannel() throws IOException
+    {
+        return new LegacyForkChannel();
+    }
 }
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/NetworkingProcessExecutor.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/NetworkingProcessExecutor.java
new file mode 100644
index 0000000..71b75cc
--- /dev/null
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/NetworkingProcessExecutor.java
@@ -0,0 +1,218 @@
+package org.apache.maven.plugin.surefire.extensions;
+
+/*
+ * 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.booter.Command;
+import org.apache.maven.surefire.booter.MasterProcessCommand;
+import org.apache.maven.surefire.extensions.CommandReader;
+import org.apache.maven.surefire.extensions.EventHandler;
+import org.apache.maven.surefire.extensions.ExecutableCommandline;
+import org.apache.maven.surefire.extensions.StdErrStreamLine;
+import org.apache.maven.surefire.extensions.StdOutStreamLine;
+import org.apache.maven.surefire.shared.utils.cli.CommandLineUtils;
+import org.apache.maven.surefire.shared.utils.cli.Commandline;
+
+import javax.annotation.Nonnull;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.AsynchronousServerSocketChannel;
+import java.nio.channels.AsynchronousSocketChannel;
+import java.nio.channels.CompletionHandler;
+import java.util.Scanner;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+
+import static java.nio.ByteBuffer.wrap;
+import static java.nio.charset.StandardCharsets.US_ASCII;
+
+/**
+ * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 3.0.0-M4
+ */
+final class NetworkingProcessExecutor implements ExecutableCommandline
+{
+    private final AsynchronousServerSocketChannel server;
+    private final ExecutorService executorService;
+
+    NetworkingProcessExecutor( AsynchronousServerSocketChannel server, ExecutorService executorService )
+    {
+        this.server = server;
+        this.executorService = executorService;
+    }
+
+    @Nonnull
+    @Override
+    public <T> Callable<Integer> executeCommandLineAsCallable( @Nonnull T cli,
+                                                               @Nonnull final CommandReader commands,
+                                                               @Nonnull final EventHandler events,
+                                                               StdOutStreamLine stdOut,
+                                                               StdErrStreamLine stdErr,
+                                                               @Nonnull Runnable runAfterProcessTermination )
+            throws Exception
+    {
+        server.accept( null, new CompletionHandler<AsynchronousSocketChannel, Object>()
+        {
+            @Override
+            public void completed( final AsynchronousSocketChannel client, Object attachment )
+            {
+                executorService.submit( new Runnable()
+                {
+                    @Override
+                    public void run()
+                    {
+                        InputStream is = toInputStream( client );
+                        try
+                        {
+                            for ( Scanner scanner = new Scanner( is, "ASCII" ); scanner.hasNextLine(); )
+                            {
+                                if ( scanner.ioException() != null )
+                                {
+                                    break;
+                                }
+                                events.handleEvent( scanner.nextLine() );
+                            }
+                        }
+                        catch ( IllegalStateException e )
+                        {
+                            // scanner and InputStream is closed
+                            try
+                            {
+                                client.close();
+                            }
+                            catch ( IOException ex )
+                            {
+                                // couldn't close the client channel
+                            }
+                        }
+                    }
+                } );
+
+                executorService.submit( new Runnable()
+                {
+                    @Override
+                    public void run()
+                    {
+                        try
+                        {
+                            for ( Command cmd; !commands.isClosed();  )
+                            {
+                                cmd = commands.readNextCommand();
+                                if ( cmd == null )
+                                {
+                                    break;
+                                }
+                                MasterProcessCommand cmdType = cmd.getCommandType();
+                                byte[] b = cmdType.hasDataType() ? cmdType.encode( cmd.getData() ) : cmdType.encode();
+                                ByteBuffer bb = wrap( b );
+                                do
+                                {
+                                    client.write( bb ).get();
+                                }
+                                while ( bb.hasRemaining() );
+                            }
+                        }
+                        catch ( Exception e )
+                        {
+                            // finished stream or error
+                            try
+                            {
+                                client.close();
+                            }
+                            catch ( IOException ex )
+                            {
+                                // couldn't close the client channel
+                            }
+                        }
+                    }
+                } );
+            }
+
+            @Override
+            public void failed( Throwable exc, Object attachment )
+            {
+                // write to dump file
+                // close the server
+            }
+        } );
+
+        return CommandLineUtils.executeCommandLineAsCallable( (Commandline) cli, null,
+                new StdOutAdapter( stdOut ), new StdErrAdapter( stdErr ), 0, runAfterProcessTermination, US_ASCII );
+    }
+
+    private static InputStream toInputStream( final AsynchronousSocketChannel client )
+    {
+        return new InputStream()
+        {
+            private final ByteBuffer bb = ByteBuffer.allocate( 64 * 1024 );
+            private boolean closed;
+
+            @Override
+            public int read() throws IOException
+            {
+                if ( closed )
+                {
+                    return -1;
+                }
+
+                try
+                {
+                    if ( !bb.hasRemaining() )
+                    {
+                        bb.clear();
+                        if ( client.read( bb ).get() == 0 )
+                        {
+                            closed = true;
+                            return -1;
+                        }
+                        bb.flip();
+                    }
+                    return bb.get();
+                }
+                catch ( InterruptedException e )
+                {
+                    closed = true;
+                    return -1;
+                }
+                catch ( ExecutionException e )
+                {
+                    closed = true;
+                    Throwable cause = e.getCause();
+                    if ( cause instanceof IOException )
+                    {
+                        throw (IOException) cause;
+                    }
+                    else
+                    {
+                        return -1;
+                    }
+                }
+            }
+
+            @Override
+            public void close() throws IOException
+            {
+                closed = true;
+                super.close();
+            }
+        };
+    }
+}
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/PipeProcessExecutor.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/PipeProcessExecutor.java
new file mode 100644
index 0000000..f6e942b
--- /dev/null
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/PipeProcessExecutor.java
@@ -0,0 +1,144 @@
+package org.apache.maven.plugin.surefire.extensions;
+
+/*
+ * 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.booter.Command;
+import org.apache.maven.surefire.booter.MasterProcessCommand;
+import org.apache.maven.surefire.extensions.CommandReader;
+import org.apache.maven.surefire.extensions.EventHandler;
+import org.apache.maven.surefire.extensions.ExecutableCommandline;
+import org.apache.maven.surefire.extensions.StdErrStreamLine;
+import org.apache.maven.surefire.extensions.StdOutStreamLine;
+import org.apache.maven.surefire.shared.utils.cli.CommandLineUtils;
+import org.apache.maven.surefire.shared.utils.cli.Commandline;
+import org.apache.maven.surefire.shared.utils.cli.StreamConsumer;
+
+import javax.annotation.Nonnull;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.concurrent.Callable;
+
+import static java.nio.charset.StandardCharsets.US_ASCII;
+
+/**
+ * Commands which are sent from plugin to the forked jvm.
+ * <br>
+ * Events are received from the forked jvm.
+ * <br>
+ * <br>
+ * magic number : opcode [: opcode specific data]*
+ * <br>
+ * or data encoded with Base64
+ * <br>
+ * magic number : opcode [: Base64(opcode specific data)]*
+ *
+ * The command and event must be finished by the character ':' and New Line.
+ *
+ * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 3.0.0-M4
+ */
+final class PipeProcessExecutor
+        implements ExecutableCommandline
+{
+    @Override
+    @Nonnull
+    public <T> Callable<Integer> executeCommandLineAsCallable( @Nonnull T cli,
+                                                               @Nonnull CommandReader commands,
+                                                               @Nonnull EventHandler events,
+                                                               StdOutStreamLine stdOut,
+                                                               StdErrStreamLine stdErr,
+                                                               @Nonnull Runnable runAfterProcessTermination )
+            throws Exception
+    {
+        return CommandLineUtils.executeCommandLineAsCallable( (Commandline) cli, new CommandReaderAdapter( commands ),
+                new EventHandlerAdapter( events ), new StdErrAdapter( stdErr ),
+                0, runAfterProcessTermination, US_ASCII );
+    }
+
+    private static class EventHandlerAdapter implements StreamConsumer
+    {
+        private final EventHandler events;
+
+        private EventHandlerAdapter( EventHandler events )
+        {
+            this.events = events;
+        }
+
+        @Override
+        public void consumeLine( String line )
+        {
+            events.handleEvent( line );
+        }
+    }
+
+    private static class CommandReaderAdapter extends InputStream
+    {
+        private final CommandReader commands;
+        private byte[] currentBuffer;
+        private int currentPos;
+        private volatile boolean closed;
+
+        CommandReaderAdapter( CommandReader commands )
+        {
+            this.commands = commands;
+        }
+
+        @Override
+        public int read() throws IOException
+        {
+            if ( commands.isClosed() )
+            {
+                close();
+            }
+
+            if ( closed )
+            {
+                return -1;
+            }
+
+            if ( currentBuffer == null )
+            {
+                Command cmd = commands.readNextCommand();
+                if ( cmd == null )
+                {
+                    currentPos = 0;
+                    return -1;
+                }
+                MasterProcessCommand cmdType = cmd.getCommandType();
+                currentBuffer = cmdType.hasDataType() ? cmdType.encode( cmd.getData() ) : cmdType.encode();
+            }
+
+            @SuppressWarnings( "checkstyle:magicnumber" )
+            int b = currentBuffer[currentPos++] & 0xff;
+            if ( currentPos == currentBuffer.length )
+            {
+                currentBuffer = null;
+                currentPos = 0;
+            }
+            return b;
+        }
+
+        @Override
+        public void close()
+        {
+            closed = true;
+        }
+    }
+}
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/MainCliOptionsAware.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/StdErrAdapter.java
similarity index 63%
copy from surefire-api/src/main/java/org/apache/maven/surefire/booter/MainCliOptionsAware.java
copy to maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/StdErrAdapter.java
index eddebed..52d352a 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/MainCliOptionsAware.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/StdErrAdapter.java
@@ -1,4 +1,4 @@
-package org.apache.maven.surefire.booter;
+package org.apache.maven.plugin.surefire.extensions;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -19,17 +19,24 @@ package org.apache.maven.surefire.booter;
  * under the License.
  */
 
-import org.apache.maven.surefire.cli.CommandLineOption;
-
-import java.util.List;
+import org.apache.maven.surefire.extensions.StdErrStreamLine;
+import org.apache.maven.surefire.shared.utils.cli.StreamConsumer;
 
 /**
- * CLI options in plugin (main) JVM process.
  *
- * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
- * @since 2.19
  */
-interface MainCliOptionsAware
+final class StdErrAdapter implements StreamConsumer
 {
-    void setMainCliOptions( List<CommandLineOption> mainCliOptions );
+    private final StdErrStreamLine stdErr;
+
+    StdErrAdapter( StdErrStreamLine stdErr )
+    {
+        this.stdErr = stdErr;
+    }
+
+    @Override
+    public void consumeLine( String line )
+    {
+        stdErr.handleLine( line );
+    }
 }
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/MainCliOptionsAware.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/StdOutAdapter.java
similarity index 63%
copy from surefire-api/src/main/java/org/apache/maven/surefire/booter/MainCliOptionsAware.java
copy to maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/StdOutAdapter.java
index eddebed..4ca8cf5 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/MainCliOptionsAware.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/StdOutAdapter.java
@@ -1,4 +1,4 @@
-package org.apache.maven.surefire.booter;
+package org.apache.maven.plugin.surefire.extensions;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -19,17 +19,24 @@ package org.apache.maven.surefire.booter;
  * under the License.
  */
 
-import org.apache.maven.surefire.cli.CommandLineOption;
-
-import java.util.List;
+import org.apache.maven.surefire.extensions.StdOutStreamLine;
+import org.apache.maven.surefire.shared.utils.cli.StreamConsumer;
 
 /**
- * CLI options in plugin (main) JVM process.
  *
- * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
- * @since 2.19
  */
-interface MainCliOptionsAware
+final class StdOutAdapter implements StreamConsumer
 {
-    void setMainCliOptions( List<CommandLineOption> mainCliOptions );
+    private final StdOutStreamLine stdOut;
+
+    StdOutAdapter( StdOutStreamLine stdOut )
+    {
+        this.stdOut = stdOut;
+    }
+
+    @Override
+    public void consumeLine( String line )
+    {
+        stdOut.handleLine( line );
+    }
 }
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireForkChannel.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireForkChannel.java
new file mode 100644
index 0000000..fb8f789
--- /dev/null
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireForkChannel.java
@@ -0,0 +1,84 @@
+package org.apache.maven.plugin.surefire.extensions;
+
+/*
+ * 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.extensions.ExecutableCommandline;
+import org.apache.maven.surefire.extensions.ForkChannel;
+
+import javax.annotation.Nonnull;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.nio.channels.AsynchronousServerSocketChannel;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import static java.net.StandardSocketOptions.SO_KEEPALIVE;
+import static java.net.StandardSocketOptions.SO_REUSEADDR;
+import static java.net.StandardSocketOptions.TCP_NODELAY;
+import static java.nio.channels.AsynchronousChannelGroup.withThreadPool;
+import static java.nio.channels.AsynchronousServerSocketChannel.open;
+import static org.apache.maven.surefire.util.internal.DaemonThreadFactory.newDaemonThreadFactory;
+
+/**
+ *
+ */
+final class SurefireForkChannel implements ForkChannel
+{
+    private final ExecutorService executorService;
+    private final AsynchronousServerSocketChannel server;
+    private final int serverPort;
+
+    SurefireForkChannel() throws IOException
+    {
+        executorService = Executors.newCachedThreadPool( newDaemonThreadFactory() );
+        server = open( withThreadPool( executorService ) );
+        server.setOption( SO_REUSEADDR, true );
+        server.setOption( TCP_NODELAY, true );
+        server.setOption( SO_KEEPALIVE, true );
+        server.bind( new InetSocketAddress( 0 ) );
+        serverPort = ( (InetSocketAddress) server.getLocalAddress() ).getPort();
+    }
+
+    @Override
+    public String getForkNodeConnectionString()
+    {
+        return "tcp://127.0.0.1:" + serverPort;
+    }
+
+    @Nonnull
+    @Override
+    public ExecutableCommandline createExecutableCommandline()
+    {
+        return new NetworkingProcessExecutor( server, executorService );
+    }
+
+    @Override
+    public void close() throws IOException
+    {
+        try
+        {
+            server.close();
+        }
+        finally
+        {
+            executorService.shutdownNow();
+        }
+    }
+}
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/MainCliOptionsAware.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireForkNodeFactory.java
similarity index 65%
copy from surefire-api/src/main/java/org/apache/maven/surefire/booter/MainCliOptionsAware.java
copy to maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireForkNodeFactory.java
index eddebed..c483619 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/MainCliOptionsAware.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireForkNodeFactory.java
@@ -1,4 +1,4 @@
-package org.apache.maven.surefire.booter;
+package org.apache.maven.plugin.surefire.extensions;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -19,17 +19,21 @@ package org.apache.maven.surefire.booter;
  * under the License.
  */
 
-import org.apache.maven.surefire.cli.CommandLineOption;
+import org.apache.maven.surefire.extensions.ForkChannel;
+import org.apache.maven.surefire.extensions.ForkNodeFactory;
 
-import java.util.List;
+import javax.annotation.Nonnull;
+import java.io.IOException;
 
 /**
- * CLI options in plugin (main) JVM process.
  *
- * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
- * @since 2.19
  */
-interface MainCliOptionsAware
+public class SurefireForkNodeFactory implements ForkNodeFactory
 {
-    void setMainCliOptions( List<CommandLineOption> mainCliOptions );
+    @Nonnull
+    @Override
+    public ForkChannel createForkChannel() throws IOException
+    {
+        return new SurefireForkChannel();
+    }
 }
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoJava7PlusTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoJava7PlusTest.java
index 9faa4eb..bc722e3 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoJava7PlusTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoJava7PlusTest.java
@@ -28,6 +28,7 @@ import org.apache.maven.surefire.booter.ClassLoaderConfiguration;
 import org.apache.maven.surefire.booter.Classpath;
 import org.apache.maven.surefire.booter.ModularClasspathConfiguration;
 import org.apache.maven.surefire.booter.StartupConfiguration;
+import org.apache.maven.surefire.extensions.ForkNodeFactory;
 import org.apache.maven.surefire.suite.RunResult;
 import org.apache.maven.surefire.util.DefaultScanResult;
 import org.codehaus.plexus.languages.java.jpms.JavaModuleDescriptor;
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoTest.java
index b1e7c1e..e70b951 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoTest.java
@@ -42,6 +42,7 @@ import org.apache.maven.shared.transfer.dependencies.resolve.DependencyResolver;
 import org.apache.maven.surefire.booter.ClassLoaderConfiguration;
 import org.apache.maven.surefire.booter.Classpath;
 import org.apache.maven.surefire.booter.StartupConfiguration;
+import org.apache.maven.surefire.extensions.ForkNodeFactory;
 import org.apache.maven.surefire.suite.RunResult;
 import org.codehaus.plexus.logging.Logger;
 import org.junit.Before;
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/CommonReflectorTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/CommonReflectorTest.java
index 1f94a75..4f954d4 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/CommonReflectorTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/CommonReflectorTest.java
@@ -23,15 +23,30 @@ import org.apache.maven.plugin.surefire.extensions.SurefireConsoleOutputReporter
 import org.apache.maven.plugin.surefire.extensions.SurefireStatelessReporter;
 import org.apache.maven.plugin.surefire.extensions.SurefireStatelessTestsetInfoReporter;
 import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
+import org.apache.maven.plugin.surefire.log.api.ConsoleLoggerDecorator;
+import org.apache.maven.plugin.surefire.log.api.PrintStreamLogger;
 import org.apache.maven.plugin.surefire.report.DefaultReporterFactory;
+import org.hamcrest.MatcherAssert;
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.ArgumentCaptor;
 
 import java.io.File;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.apache.maven.surefire.util.ReflectionUtils.getMethod;
+import static org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray;
 import static org.fest.assertions.Assertions.assertThat;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.CoreMatchers.sameInstance;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 import static org.powermock.reflect.Whitebox.getInternalState;
 
 /**
@@ -96,4 +111,39 @@ public class CommonReflectorTest
         assertThat( reportConfiguration.getConsoleOutputReporter().toString() )
                 .isEqualTo( consoleOutputReporter.toString() );
     }
+
+    @Test
+    public void shouldProxyConsoleLogger()
+    {
+        ClassLoader cl = Thread.currentThread().getContextClassLoader();
+        ConsoleLogger logger = spy( new PrintStreamLogger( System.out ) );
+        Object mirror = CommonReflector.createConsoleLogger( logger, cl );
+        MatcherAssert.assertThat( mirror, is( notNullValue() ) );
+        MatcherAssert.assertThat( mirror.getClass().getInterfaces()[0].getName(), is( ConsoleLogger.class.getName() ) );
+        MatcherAssert.assertThat( mirror, is( not( sameInstance( (Object) logger ) ) ) );
+        MatcherAssert.assertThat( mirror, is( instanceOf( ConsoleLoggerDecorator.class ) ) );
+        invokeMethodWithArray( mirror, getMethod( mirror, "info", String.class ), "Hi There!" );
+        verify( logger, times( 1 ) ).info( "Hi There!" );
+    }
+
+    @Test
+    public void testCreateConsoleLogger()
+    {
+        ClassLoader cl = Thread.currentThread().getContextClassLoader();
+        ConsoleLogger consoleLogger = mock( ConsoleLogger.class );
+        ConsoleLogger decorator = (ConsoleLogger) CommonReflector.createConsoleLogger( consoleLogger, cl );
+        assertThat( decorator )
+                .isNotSameAs( consoleLogger );
+
+        assertThat( decorator.isDebugEnabled() ).isFalse();
+        when( consoleLogger.isDebugEnabled() ).thenReturn( true );
+        assertThat( decorator.isDebugEnabled() ).isTrue();
+        verify( consoleLogger, times( 2 ) ).isDebugEnabled();
+
+        decorator.info( "msg" );
+        ArgumentCaptor<String> argumentMsg = ArgumentCaptor.forClass( String.class );
+        verify( consoleLogger, times( 1 ) ).info( argumentMsg.capture() );
+        assertThat( argumentMsg.getAllValues() ).hasSize( 1 );
+        assertThat( argumentMsg.getAllValues().get( 0 ) ).isEqualTo( "msg" );
+    }
 }
\ No newline at end of file
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/MojoMocklessTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/MojoMocklessTest.java
index ccf63f7..835ce8e 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/MojoMocklessTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/MojoMocklessTest.java
@@ -28,6 +28,7 @@ import org.apache.maven.plugin.MojoFailureException;
 import org.apache.maven.plugin.surefire.extensions.SurefireConsoleOutputReporter;
 import org.apache.maven.plugin.surefire.extensions.SurefireStatelessReporter;
 import org.apache.maven.plugin.surefire.extensions.SurefireStatelessTestsetInfoReporter;
+import org.apache.maven.surefire.extensions.ForkNodeFactory;
 import org.apache.maven.surefire.suite.RunResult;
 import org.apache.maven.surefire.util.DefaultScanResult;
 import org.apache.maven.toolchain.Toolchain;
@@ -751,6 +752,12 @@ public class MojoMocklessTest
         }
 
         @Override
+        protected ForkNodeFactory getForkNode()
+        {
+            return null;
+        }
+
+        @Override
         protected String getEnableProcessChecker()
         {
             return null;
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/SurefireReflectorTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/SurefireReflectorTest.java
deleted file mode 100644
index 2553617..0000000
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/SurefireReflectorTest.java
+++ /dev/null
@@ -1,71 +0,0 @@
-package org.apache.maven.plugin.surefire;
-
-/*
- * 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.plugin.surefire.log.api.ConsoleLogger;
-import org.apache.maven.plugin.surefire.log.api.ConsoleLoggerDecorator;
-import org.apache.maven.plugin.surefire.log.api.PrintStreamLogger;
-import org.apache.maven.surefire.booter.IsolatedClassLoader;
-import org.apache.maven.surefire.booter.SurefireReflector;
-import org.junit.Before;
-import org.junit.Test;
-
-import static org.apache.maven.surefire.util.ReflectionUtils.getMethod;
-import static org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray;
-import static org.hamcrest.CoreMatchers.instanceOf;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.CoreMatchers.notNullValue;
-import static org.hamcrest.CoreMatchers.sameInstance;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-/**
- * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
- * @see ConsoleLogger
- * @see SurefireReflector
- * @since 2.20
- */
-public class SurefireReflectorTest
-{
-    private ConsoleLogger logger;
-    private ClassLoader cl;
-
-    @Before
-    public void prepareData()
-    {
-        logger = spy( new PrintStreamLogger( System.out ) );
-        cl = new IsolatedClassLoader( Thread.currentThread().getContextClassLoader(), false, "role" );
-    }
-
-    @Test
-    public void shouldProxyConsoleLogger()
-    {
-        Object mirror = SurefireReflector.createConsoleLogger( logger, cl );
-        assertThat( mirror, is( notNullValue() ) );
-        assertThat( mirror.getClass().getInterfaces()[0].getName(), is( ConsoleLogger.class.getName() ) );
-        assertThat( mirror, is( not( sameInstance( (Object) logger ) ) ) );
-        assertThat( mirror, is( instanceOf( ConsoleLoggerDecorator.class ) ) );
-        invokeMethodWithArray( mirror, getMethod( mirror, "info", String.class ), "Hi There!" );
-        verify( logger, times( 1 ) ).info( "Hi There!" );
-    }
-}
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 ca42c44..caf1376 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
@@ -260,9 +260,10 @@ public class BooterDeserializerProviderConfigurationTest
             test = "aTest";
         }
         final File propsTest = booterSerializer.serialize( props, booterConfiguration, testProviderConfiguration, test,
-                                                           readTestsFromInStream, 51L, 1 );
+                                                           readTestsFromInStream, 51L, 1, "pipe://" );
         BooterDeserializer booterDeserializer = new BooterDeserializer( new FileInputStream( propsTest ) );
         assertEquals( "51", (Object) booterDeserializer.getPluginPid() );
+        assertEquals( "pipe://", booterDeserializer.getForkNodeConnectionString() );
         return booterDeserializer.deserialize();
     }
 
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 4e9bbc9..5054e6c 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
@@ -178,9 +178,10 @@ public class BooterDeserializerStartupConfigurationTest
         BooterSerializer booterSerializer = new BooterSerializer( forkConfiguration );
         String aTest = "aTest";
         File propsTest = booterSerializer.serialize( props, getProviderConfiguration(), startupConfiguration, aTest,
-                false, null, 1 );
+                false, null, 1, null );
         BooterDeserializer booterDeserializer = new BooterDeserializer( new FileInputStream( propsTest ) );
         assertNull( booterDeserializer.getPluginPid() );
+        assertNull( booterDeserializer.getForkNodeConnectionString() );
         return booterDeserializer.getStartupConfiguration();
     }
 
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/DefaultForkConfigurationTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/DefaultForkConfigurationTest.java
index 06fe754..f0d3482 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/DefaultForkConfigurationTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/DefaultForkConfigurationTest.java
@@ -27,7 +27,7 @@ import org.apache.maven.surefire.booter.Classpath;
 import org.apache.maven.surefire.booter.ClasspathConfiguration;
 import org.apache.maven.surefire.booter.ForkedBooter;
 import org.apache.maven.surefire.booter.StartupConfiguration;
-import org.apache.maven.surefire.booter.SurefireBooterForkException;
+import org.apache.maven.surefire.extensions.ForkNodeFactory;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -80,6 +80,7 @@ public class DefaultForkConfigurationTest
     private boolean reuseForks;
     private Platform pluginPlatform;
     private ConsoleLogger log;
+    private ForkNodeFactory forkNodeFactory;
 
     @Before
     public void setup()
@@ -97,6 +98,7 @@ public class DefaultForkConfigurationTest
         reuseForks = true;
         pluginPlatform = new Platform();
         log = mock( ConsoleLogger.class );
+        forkNodeFactory = mock( ForkNodeFactory.class );
     }
 
     @Test
@@ -104,16 +106,15 @@ public class DefaultForkConfigurationTest
     {
         DefaultForkConfiguration config = new DefaultForkConfiguration( booterClasspath, tempDirectory, debugLine,
                 workingDirectory, modelProperties, argLine, environmentVariables, excludedEnvironmentVariables,
-                debug, forkCount, reuseForks, pluginPlatform, log )
+                debug, forkCount, reuseForks, pluginPlatform, log, forkNodeFactory )
         {
 
             @Override
             protected void resolveClasspath( @Nonnull OutputStreamFlushableCommandline cli,
                                              @Nonnull String booterThatHasMainMethod,
                                              @Nonnull StartupConfiguration config,
-                                             @Nonnull File dumpLogDirectory ) throws SurefireBooterForkException
+                                             @Nonnull File dumpLogDirectory )
             {
-
             }
         };
 
@@ -130,16 +131,15 @@ public class DefaultForkConfigurationTest
         argLine = "";
         DefaultForkConfiguration config = new DefaultForkConfiguration( booterClasspath, tempDirectory, debugLine,
                 workingDirectory, modelProperties, argLine, environmentVariables, excludedEnvironmentVariables,
-                debug, forkCount, reuseForks, pluginPlatform, log )
+                debug, forkCount, reuseForks, pluginPlatform, log, forkNodeFactory )
         {
 
             @Override
             protected void resolveClasspath( @Nonnull OutputStreamFlushableCommandline cli,
                                              @Nonnull String booterThatHasMainMethod,
                                              @Nonnull StartupConfiguration config,
-                                             @Nonnull File dumpLogDirectory ) throws SurefireBooterForkException
+                                             @Nonnull File dumpLogDirectory )
             {
-
             }
         };
 
@@ -156,16 +156,15 @@ public class DefaultForkConfigurationTest
         argLine = "\n\r";
         DefaultForkConfiguration config = new DefaultForkConfiguration( booterClasspath, tempDirectory, debugLine,
                 workingDirectory, modelProperties, argLine, environmentVariables, excludedEnvironmentVariables,
-                debug, forkCount, reuseForks, pluginPlatform, log )
+                debug, forkCount, reuseForks, pluginPlatform, log, forkNodeFactory )
         {
 
             @Override
             protected void resolveClasspath( @Nonnull OutputStreamFlushableCommandline cli,
                                              @Nonnull String booterThatHasMainMethod,
                                              @Nonnull StartupConfiguration config,
-                                             @Nonnull File dumpLogDirectory ) throws SurefireBooterForkException
+                                             @Nonnull File dumpLogDirectory )
             {
-
             }
         };
 
@@ -182,16 +181,15 @@ public class DefaultForkConfigurationTest
         argLine = "-Dfile.encoding=UTF-8";
         DefaultForkConfiguration config = new DefaultForkConfiguration( booterClasspath, tempDirectory, debugLine,
                 workingDirectory, modelProperties, argLine, environmentVariables, excludedEnvironmentVariables,
-                debug, forkCount, reuseForks, pluginPlatform, log )
+                debug, forkCount, reuseForks, pluginPlatform, log, forkNodeFactory )
         {
 
             @Override
             protected void resolveClasspath( @Nonnull OutputStreamFlushableCommandline cli,
                                              @Nonnull String booterThatHasMainMethod,
                                              @Nonnull StartupConfiguration config,
-                                             @Nonnull File dumpLogDirectory ) throws SurefireBooterForkException
+                                             @Nonnull File dumpLogDirectory )
             {
-
             }
         };
 
@@ -209,16 +207,15 @@ public class DefaultForkConfigurationTest
         argLine = "-Dfile.encoding=@{encoding}";
         DefaultForkConfiguration config = new DefaultForkConfiguration( booterClasspath, tempDirectory, debugLine,
                 workingDirectory, modelProperties, argLine, environmentVariables, excludedEnvironmentVariables,
-                debug, forkCount, reuseForks, pluginPlatform, log )
+                debug, forkCount, reuseForks, pluginPlatform, log, forkNodeFactory )
         {
 
             @Override
             protected void resolveClasspath( @Nonnull OutputStreamFlushableCommandline cli,
                                              @Nonnull String booterThatHasMainMethod,
                                              @Nonnull StartupConfiguration config,
-                                             @Nonnull File dumpLogDirectory ) throws SurefireBooterForkException
+                                             @Nonnull File dumpLogDirectory )
             {
-
             }
         };
 
@@ -235,16 +232,15 @@ public class DefaultForkConfigurationTest
         argLine = "a\n\rb";
         DefaultForkConfiguration config = new DefaultForkConfiguration( booterClasspath, tempDirectory, debugLine,
                 workingDirectory, modelProperties, argLine, environmentVariables, excludedEnvironmentVariables,
-                debug, forkCount, reuseForks, pluginPlatform, log )
+                debug, forkCount, reuseForks, pluginPlatform, log, forkNodeFactory )
         {
 
             @Override
             protected void resolveClasspath( @Nonnull OutputStreamFlushableCommandline cli,
                                              @Nonnull String booterThatHasMainMethod,
                                              @Nonnull StartupConfiguration config,
-                                             @Nonnull File dumpLogDirectory ) throws SurefireBooterForkException
+                                             @Nonnull File dumpLogDirectory )
             {
-
             }
         };
 
@@ -261,16 +257,15 @@ public class DefaultForkConfigurationTest
         argLine = "-Dthread=${surefire.threadNumber}";
         DefaultForkConfiguration config = new DefaultForkConfiguration( booterClasspath, tempDirectory, debugLine,
                 workingDirectory, modelProperties, argLine, environmentVariables, excludedEnvironmentVariables,
-                debug, forkCount, reuseForks, pluginPlatform, log )
+                debug, forkCount, reuseForks, pluginPlatform, log, forkNodeFactory )
         {
 
             @Override
             protected void resolveClasspath( @Nonnull OutputStreamFlushableCommandline cli,
                                              @Nonnull String booterThatHasMainMethod,
                                              @Nonnull StartupConfiguration config,
-                                             @Nonnull File dumpLogDirectory ) throws SurefireBooterForkException
+                                             @Nonnull File dumpLogDirectory )
             {
-
             }
         };
 
@@ -287,16 +282,15 @@ public class DefaultForkConfigurationTest
         argLine = "-Dthread=${surefire.forkNumber}";
         DefaultForkConfiguration config = new DefaultForkConfiguration( booterClasspath, tempDirectory, debugLine,
                 workingDirectory, modelProperties, argLine, environmentVariables, excludedEnvironmentVariables,
-                debug, forkCount, reuseForks, pluginPlatform, log )
+                debug, forkCount, reuseForks, pluginPlatform, log, forkNodeFactory )
         {
 
             @Override
             protected void resolveClasspath( @Nonnull OutputStreamFlushableCommandline cli,
                                              @Nonnull String booterThatHasMainMethod,
                                              @Nonnull StartupConfiguration config,
-                                             @Nonnull File dumpLogDirectory ) throws SurefireBooterForkException
+                                             @Nonnull File dumpLogDirectory )
             {
-
             }
         };
 
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkConfigurationTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkConfigurationTest.java
index 72e5372..16564a3 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkConfigurationTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkConfigurationTest.java
@@ -30,6 +30,7 @@ import org.apache.maven.surefire.booter.Classpath;
 import org.apache.maven.surefire.booter.ClasspathConfiguration;
 import org.apache.maven.surefire.booter.StartupConfiguration;
 import org.apache.maven.surefire.booter.SurefireBooterForkException;
+import org.apache.maven.surefire.extensions.ForkNodeFactory;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -44,9 +45,8 @@ import static java.util.Collections.singletonList;
 import static org.apache.maven.surefire.booter.Classpath.emptyClasspath;
 import static org.apache.maven.surefire.booter.ProcessCheckerType.ALL;
 import static org.fest.util.Files.temporaryFolder;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.mock;
 
 /**
  *
@@ -225,7 +225,7 @@ public class ForkConfigurationTest
         return new JarManifestForkConfiguration( emptyClasspath(), tmpDir, null,
                 cwd, new Properties(), argLine,
                 Collections.<String, String>emptyMap(), new String[0], false, 1, false,
-                platform, new NullConsoleLogger() );
+                platform, new NullConsoleLogger(), mock( ForkNodeFactory.class ) );
     }
 
     // based on http://stackoverflow.com/questions/2591083/getting-version-of-java-in-runtime
@@ -233,6 +233,6 @@ public class ForkConfigurationTest
     private static boolean isJavaVersionAtLeast7u60()
     {
         String[] javaVersionElements = System.getProperty( "java.runtime.version" ).split( "\\.|_|-b" );
-        return Integer.valueOf( javaVersionElements[1] ) >= 7 && Integer.valueOf( javaVersionElements[3] ) >= 60;
+        return Integer.parseInt( javaVersionElements[1] ) >= 7 && Integer.parseInt( javaVersionElements[3] ) >= 60;
     }
 }
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ModularClasspathForkConfigurationTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ModularClasspathForkConfigurationTest.java
index 492c5c0..77cc49d 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ModularClasspathForkConfigurationTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ModularClasspathForkConfigurationTest.java
@@ -66,7 +66,7 @@ public class ModularClasspathForkConfigurationTest
         ModularClasspathForkConfiguration config = new ModularClasspathForkConfiguration( booter, tmp, "", pwd,
                 new Properties(), "",
                 Collections.<String, String>emptyMap(), new String[0], true, 1, true,
-                new Platform(), new NullConsoleLogger() );
+                new Platform(), new NullConsoleLogger(), null );
 
         File patchFile = new File( "target" + separatorChar + "test-classes" );
         File descriptor = new File( tmp, "module-info.class" );
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestLessInputStreamBuilderTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestLessInputStreamBuilderTest.java
index bbc85d4..39789ac 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestLessInputStreamBuilderTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestLessInputStreamBuilderTest.java
@@ -21,12 +21,14 @@ package org.apache.maven.plugin.surefire.booterclient.lazytestprovider;
 
 import org.apache.maven.surefire.booter.Command;
 import org.apache.maven.surefire.booter.MasterProcessCommand;
+import org.apache.maven.surefire.booter.spi.DefaultMasterProcessChannelDecoder;
+import org.apache.maven.surefire.providerapi.MasterProcessChannelDecoder;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
 
-import java.io.DataInputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.util.Iterator;
 import java.util.NoSuchElementException;
 
@@ -34,7 +36,6 @@ import static org.apache.maven.plugin.surefire.booterclient.lazytestprovider.Tes
 import static org.apache.maven.surefire.booter.Command.NOOP;
 import static org.apache.maven.surefire.booter.Command.SKIP_SINCE_NEXT_TEST;
 import static org.apache.maven.surefire.booter.MasterProcessCommand.SHUTDOWN;
-import static org.apache.maven.surefire.booter.MasterProcessCommand.decode;
 import static org.apache.maven.surefire.booter.Shutdown.EXIT;
 import static org.apache.maven.surefire.booter.Shutdown.KILL;
 import static org.hamcrest.MatcherAssert.assertThat;
@@ -137,15 +138,43 @@ public class TestLessInputStreamBuilderTest
             throws IOException
     {
         TestLessInputStreamBuilder builder = new TestLessInputStreamBuilder();
-        TestLessInputStream pluginIs = builder.build();
+        final TestLessInputStream pluginIs = builder.build();
+        InputStream is = new InputStream()
+        {
+            private byte[] buffer;
+            private int idx;
+
+            @Override
+            public int read() throws IOException
+            {
+                if ( buffer == null )
+                {
+                    idx = 0;
+                    Command cmd = pluginIs.readNextCommand();
+                    buffer = cmd == null ? null : cmd.getCommandType().encode();
+                }
+
+                if ( buffer != null )
+                {
+                    byte b = buffer[idx++];
+                    if ( idx == buffer.length )
+                    {
+                        buffer = null;
+                        idx = 0;
+                    }
+                    return b;
+                }
+                throw new IOException();
+            }
+        };
+        MasterProcessChannelDecoder decoder = new DefaultMasterProcessChannelDecoder( is, null );
         builder.getImmediateCommands().shutdown( KILL );
         builder.getImmediateCommands().noop();
-        DataInputStream is = new DataInputStream( pluginIs );
-        Command bye = decode( is );
+        Command bye = decoder.decode();
         assertThat( bye, is( notNullValue() ) );
         assertThat( bye.getCommandType(), is( SHUTDOWN ) );
         assertThat( bye.getData(), is( KILL.name() ) );
-        Command noop = decode( is );
+        Command noop = decoder.decode();
         assertThat( noop, is( notNullValue() ) );
         assertThat( noop.getCommandType(), is( MasterProcessCommand.NOOP ) );
     }
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStreamTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStreamTest.java
index 21bc663..f863be1 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStreamTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStreamTest.java
@@ -20,11 +20,13 @@ package org.apache.maven.plugin.surefire.booterclient.lazytestprovider;
  */
 
 import org.apache.maven.surefire.booter.Command;
-import org.apache.maven.surefire.booter.MasterProcessCommand;
+import org.apache.maven.surefire.booter.spi.DefaultMasterProcessChannelDecoder;
+import org.apache.maven.surefire.providerapi.MasterProcessChannelDecoder;
 import org.junit.Test;
 
-import java.io.DataInputStream;
 import java.io.IOException;
+import java.io.InputStream;
+import java.lang.Thread.State;
 import java.util.ArrayDeque;
 import java.util.Queue;
 import java.util.concurrent.Callable;
@@ -32,11 +34,13 @@ import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.FutureTask;
 import java.util.concurrent.TimeUnit;
 
+import static java.nio.charset.StandardCharsets.US_ASCII;
 import static org.apache.maven.surefire.booter.MasterProcessCommand.BYE_ACK;
-import static org.apache.maven.surefire.booter.MasterProcessCommand.decode;
+import static org.apache.maven.surefire.booter.MasterProcessCommand.NOOP;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.notNullValue;
+import static org.junit.Assert.assertTrue;
 
 /**
  * Asserts that this stream properly reads bytes from queue.
@@ -47,13 +51,13 @@ import static org.hamcrest.Matchers.notNullValue;
 public class TestProvidingInputStreamTest
 {
     @Test
-    public void closedStreamShouldReturnEndOfStream()
+    public void closedStreamShouldReturnNullAsEndOfStream()
         throws IOException
     {
         Queue<String> commands = new ArrayDeque<>();
         TestProvidingInputStream is = new TestProvidingInputStream( commands );
         is.close();
-        assertThat( is.read(), is( -1 ) );
+        assertThat( is.readNextCommand(), is( nullValue() ) );
     }
 
     @Test
@@ -63,22 +67,22 @@ public class TestProvidingInputStreamTest
         Queue<String> commands = new ArrayDeque<>();
         final TestProvidingInputStream is = new TestProvidingInputStream( commands );
         final Thread streamThread = Thread.currentThread();
-        FutureTask<Thread.State> futureTask = new FutureTask<>( new Callable<Thread.State>()
+        FutureTask<State> futureTask = new FutureTask<>( new Callable<State>()
         {
             @Override
-            public Thread.State call()
+            public State call()
             {
-                sleep( 1000 );
-                Thread.State state = streamThread.getState();
+                sleep( 1000L );
+                State state = streamThread.getState();
                 is.close();
                 return state;
             }
         } );
         Thread assertionThread = new Thread( futureTask );
         assertionThread.start();
-        assertThat( is.read(), is( -1 ) );
-        Thread.State state = futureTask.get();
-        assertThat( state, is( Thread.State.WAITING ) );
+        assertThat( is.readNextCommand(), is( nullValue() ) );
+        State state = futureTask.get();
+        assertThat( state, is( State.WAITING ) );
     }
 
     @Test
@@ -96,16 +100,22 @@ public class TestProvidingInputStreamTest
                 is.provideNewTest();
             }
         } ).start();
-        assertThat( is.read(), is( 0 ) );
-        assertThat( is.read(), is( 0 ) );
-        assertThat( is.read(), is( 0 ) );
-        assertThat( is.read(), is( 1 ) );
-        assertThat( is.read(), is( 0 ) );
-        assertThat( is.read(), is( 0 ) );
-        assertThat( is.read(), is( 0 ) );
-        assertThat( is.read(), is( 0 ) );
+
+        StringBuilder stream = new StringBuilder();
+        for ( int i = 0; i < 82; i++ )
+        {
+            Command cmd = is.readNextCommand();
+            assertThat( cmd.getData(), is( nullValue() ) );
+            stream.append( new String( cmd.getCommandType().encode(), US_ASCII ) );
+        }
+        assertThat( stream.toString(),
+                is( ":maven-surefire-std-out:testset-finished::maven-surefire-std-out:testset-finished:" ) );
+
+        boolean emptyStream = isInputStreamEmpty( is );
+
         is.close();
-        assertThat( is.read(), is( -1 ) );
+        assertTrue( emptyStream );
+        assertThat( is.readNextCommand(), is( nullValue() ) );
     }
 
     @Test
@@ -123,34 +133,62 @@ public class TestProvidingInputStreamTest
                 is.provideNewTest();
             }
         } ).start();
-        assertThat( is.read(), is( 0 ) );
-        assertThat( is.read(), is( 0 ) );
-        assertThat( is.read(), is( 0 ) );
-        assertThat( is.read(), is( 0 ) );
-        assertThat( is.read(), is( 0 ) );
-        assertThat( is.read(), is( 0 ) );
-        assertThat( is.read(), is( 0 ) );
-        assertThat( is.read(), is( 4 ) );
-        assertThat( is.read(), is( (int) 'T' ) );
-        assertThat( is.read(), is( (int) 'e' ) );
-        assertThat( is.read(), is( (int) 's' ) );
-        assertThat( is.read(), is( (int) 't' ) );
+
+        StringBuilder stream = new StringBuilder();
+        for ( int i = 0; i < 43; i++ )
+        {
+            Command cmd = is.readNextCommand();
+            assertThat( cmd.getData(), is( nullValue() ) );
+            stream.append( new String( is.readNextCommand().getCommandType().encode(), US_ASCII ) );
+        }
+        assertThat( stream.toString(),
+                is( ":maven-surefire-std-out:run-testclass:Test:" ) );
+
+        is.close();
     }
 
     @Test
     public void shouldDecodeTwoCommands()
             throws IOException
     {
-        TestProvidingInputStream pluginIs = new TestProvidingInputStream( new ConcurrentLinkedQueue<String>() );
+        final TestProvidingInputStream pluginIs = new TestProvidingInputStream( new ConcurrentLinkedQueue<String>() );
+        InputStream is = new InputStream()
+        {
+            private byte[] buffer;
+            private int idx;
+
+            @Override
+            public int read() throws IOException
+            {
+                if ( buffer == null )
+                {
+                    idx = 0;
+                    Command cmd = pluginIs.readNextCommand();
+                    buffer = cmd == null ? null : cmd.getCommandType().encode();
+                }
+
+                if ( buffer != null )
+                {
+                    byte b = buffer[idx++];
+                    if ( idx == buffer.length )
+                    {
+                        buffer = null;
+                        idx = 0;
+                    }
+                    return b;
+                }
+                throw new IOException();
+            }
+        };
+        MasterProcessChannelDecoder decoder = new DefaultMasterProcessChannelDecoder( is, null );
         pluginIs.acknowledgeByeEventReceived();
         pluginIs.noop();
-        DataInputStream is = new DataInputStream( pluginIs );
-        Command bye = decode( is );
+        Command bye = decoder.decode();
         assertThat( bye, is( notNullValue() ) );
         assertThat( bye.getCommandType(), is( BYE_ACK ) );
-        Command noop = decode( is );
+        Command noop = decoder.decode();
         assertThat( noop, is( notNullValue() ) );
-        assertThat( noop.getCommandType(), is( MasterProcessCommand.NOOP ) );
+        assertThat( noop.getCommandType(), is( NOOP ) );
     }
 
     private static void sleep( long millis )
@@ -164,4 +202,40 @@ public class TestProvidingInputStreamTest
             // do nothing
         }
     }
+
+    /**
+     * Waiting (max of 20 seconds)
+     * @param is examined stream
+     * @return {@code true} if the {@link InputStream#read()} is waiting for a new byte.
+     */
+    private static boolean isInputStreamEmpty( final TestProvidingInputStream is )
+    {
+        Thread t = new Thread( new Runnable()
+        {
+            @Override
+            public void run()
+            {
+                try
+                {
+                    is.readNextCommand();
+                }
+                catch ( IOException e )
+                {
+                    Throwable cause = e.getCause();
+                    Throwable err = cause == null ? e : cause;
+                    System.err.println( err.toString() );
+                }
+            }
+        } );
+        t.start();
+        State state;
+        int loops = 0;
+        do
+        {
+            sleep( 100L );
+            state = t.getState();
+        } while ( state == State.NEW && loops++ < 200 );
+        t.interrupt();
+        return state == State.WAITING || state == State.TIMED_WAITING;
+    }
 }
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/surefire/JUnit4SuiteTest.java b/maven-surefire-common/src/test/java/org/apache/maven/surefire/JUnit4SuiteTest.java
index b1e3b22..21cee75 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/surefire/JUnit4SuiteTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/surefire/JUnit4SuiteTest.java
@@ -28,7 +28,6 @@ import org.apache.maven.plugin.surefire.AbstractSurefireMojoTest;
 import org.apache.maven.plugin.surefire.CommonReflectorTest;
 import org.apache.maven.plugin.surefire.MojoMocklessTest;
 import org.apache.maven.plugin.surefire.SurefireHelperTest;
-import org.apache.maven.plugin.surefire.SurefireReflectorTest;
 import org.apache.maven.plugin.surefire.SurefirePropertiesTest;
 import org.apache.maven.plugin.surefire.booterclient.BooterDeserializerProviderConfigurationTest;
 import org.apache.maven.plugin.surefire.booterclient.BooterDeserializerStartupConfigurationTest;
@@ -89,7 +88,6 @@ public class JUnit4SuiteTest extends TestCase
         suite.addTest( new JUnit4TestAdapter( TestProvidingInputStreamTest.class ) );
         suite.addTest( new JUnit4TestAdapter( TestLessInputStreamBuilderTest.class ) );
         suite.addTest( new JUnit4TestAdapter( SPITest.class ) );
-        suite.addTest( new JUnit4TestAdapter( SurefireReflectorTest.class ) );
         suite.addTest( new JUnit4TestAdapter( SurefireHelperTest.class ) );
         suite.addTest( new JUnit4TestAdapter( AbstractSurefireMojoTest.class ) );
         suite.addTest( new JUnit4TestAdapter( DefaultForkConfigurationTest.class ) );
diff --git a/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefirePlugin.java b/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefirePlugin.java
index b39181a..5f6edea 100644
--- a/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefirePlugin.java
+++ b/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefirePlugin.java
@@ -29,6 +29,7 @@ import org.apache.maven.plugins.annotations.LifecyclePhase;
 import org.apache.maven.plugins.annotations.Mojo;
 import org.apache.maven.plugins.annotations.Parameter;
 import org.apache.maven.plugins.annotations.ResolutionScope;
+import org.apache.maven.surefire.extensions.ForkNodeFactory;
 import org.apache.maven.surefire.suite.RunResult;
 
 import static org.apache.maven.plugin.surefire.SurefireHelper.reportExecution;
@@ -365,6 +366,9 @@ public class SurefirePlugin
     @Parameter( property = "surefire.useModulePath", defaultValue = "true" )
     private boolean useModulePath;
 
+    @Parameter( property = "surefire.forkNode" )
+    private ForkNodeFactory forkNode;
+
     /**
      * You can selectively exclude individual environment variables by enumerating their keys.
      * <br>
@@ -831,4 +835,10 @@ public class SurefirePlugin
     {
         return enableProcessChecker;
     }
+
+    @Override
+    protected final ForkNodeFactory getForkNode()
+    {
+        return forkNode;
+    }
 }
diff --git a/pom.xml b/pom.xml
index 1bcb356..49f0b14 100644
--- a/pom.xml
+++ b/pom.xml
@@ -51,6 +51,7 @@
     <module>surefire-logger-api</module>
     <module>surefire-api</module>
     <module>surefire-extensions-api</module>
+    <module>surefire-extensions-spi</module>
     <module>surefire-booter</module>
     <module>surefire-grouper</module>
     <module>surefire-providers</module>
@@ -474,10 +475,8 @@
               </goals>
               <configuration>
                 <includes>
-                  <include>org/apache/maven/shared/utils/logging/*.java</include>
                   <include>HelpMojo.java</include>
                   <include>**/HelpMojo.java</include>
-                  <include>org/apache/maven/plugin/failsafe/xmlsummary/*.java</include>
                 </includes>
                 <compilerArgs>
                   <!-- FIXME: maven-plugin-plugin therefore used -syntax or none due to HelpMojo -->
@@ -493,14 +492,13 @@
               </goals>
               <configuration>
                 <excludes>
-                  <exclude>org/apache/maven/shared/utils/logging/*.java</exclude>
                   <exclude>HelpMojo.java</exclude>
                   <exclude>**/HelpMojo.java</exclude>
-                  <exclude>org/apache/maven/plugin/failsafe/xmlsummary/*.java</exclude>
                 </excludes>
                 <compilerArgs>
                   <arg>-Xdoclint:all</arg>
                 </compilerArgs>
+                <verbose>true</verbose>
               </configuration>
             </execution>
           </executions>
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/BaseProviderFactory.java b/surefire-api/src/main/java/org/apache/maven/surefire/booter/BaseProviderFactory.java
index ec05580..9d2dc73 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/BaseProviderFactory.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/booter/BaseProviderFactory.java
@@ -20,6 +20,7 @@ package org.apache.maven.surefire.booter;
  */
 
 import org.apache.maven.surefire.cli.CommandLineOption;
+import org.apache.maven.surefire.providerapi.CommandChainReader;
 import org.apache.maven.surefire.providerapi.ProviderParameters;
 import org.apache.maven.surefire.report.ConsoleStream;
 import org.apache.maven.surefire.report.DefaultDirectConsoleReporter;
@@ -46,14 +47,12 @@ import static java.util.Collections.emptyList;
  * @author Kristian Rosenvold
  */
 public class BaseProviderFactory
-    implements DirectoryScannerParametersAware, ReporterConfigurationAware, SurefireClassLoadersAware, TestRequestAware,
-    ProviderPropertiesAware, ProviderParameters, TestArtifactInfoAware, RunOrderParametersAware, MainCliOptionsAware,
-    FailFastAware, ShutdownAware
+    implements ProviderParameters
 {
-    private final ReporterFactory reporterFactory;
-
     private final boolean insideFork;
 
+    private ReporterFactory reporterFactory;
+
     private ForkedChannelEncoder forkedChannelEncoder;
 
     private List<CommandLineOption> mainCliOptions = emptyList();
@@ -74,17 +73,27 @@ public class BaseProviderFactory
 
     private int skipAfterFailureCount;
 
-    private Shutdown shutdown;
-
     private Integer systemExitTimeout;
 
-    public BaseProviderFactory( ReporterFactory reporterFactory, boolean insideFork )
+    private CommandChainReader commandReader;
+
+    public BaseProviderFactory( boolean insideFork )
     {
-        this.reporterFactory = reporterFactory;
         this.insideFork = insideFork;
     }
 
     @Override
+    public CommandChainReader getCommandReader()
+    {
+        return commandReader;
+    }
+
+    public void setCommandReader( CommandChainReader commandReader )
+    {
+        this.commandReader = commandReader;
+    }
+
+    @Override
     @Deprecated
     public DirectoryScanner getDirectoryScanner()
     {
@@ -114,25 +123,27 @@ public class BaseProviderFactory
                 ? null : new DefaultRunOrderCalculator( runOrderParameters, getThreadCount() );
     }
 
+    public void setReporterFactory( ReporterFactory reporterFactory )
+    {
+        this.reporterFactory = reporterFactory;
+    }
+
     @Override
     public ReporterFactory getReporterFactory()
     {
         return reporterFactory;
     }
 
-    @Override
     public void setDirectoryScannerParameters( DirectoryScannerParameters directoryScannerParameters )
     {
         this.directoryScannerParameters = directoryScannerParameters;
     }
 
-    @Override
     public void setReporterConfiguration( ReporterConfiguration reporterConfiguration )
     {
         this.reporterConfiguration = reporterConfiguration;
     }
 
-    @Override
     public void setClassLoaders( ClassLoader testClassLoader )
     {
         this.testClassLoader = testClassLoader;
@@ -145,7 +156,6 @@ public class BaseProviderFactory
                        : new DefaultDirectConsoleReporter( reporterConfiguration.getOriginalSystemOut() );
     }
 
-    @Override
     public void setTestRequest( TestRequest testRequest )
     {
         this.testRequest = testRequest;
@@ -175,7 +185,6 @@ public class BaseProviderFactory
         return testClassLoader;
     }
 
-    @Override
     public void setProviderProperties( Map<String, String> providerProperties )
     {
         this.providerProperties = providerProperties;
@@ -193,13 +202,11 @@ public class BaseProviderFactory
         return testArtifactInfo;
     }
 
-    @Override
     public void setTestArtifactInfo( TestArtifactInfo testArtifactInfo )
     {
         this.testArtifactInfo = testArtifactInfo;
     }
 
-    @Override
     public void setRunOrderParameters( RunOrderParameters runOrderParameters )
     {
         this.runOrderParameters = runOrderParameters;
@@ -211,7 +218,11 @@ public class BaseProviderFactory
         return mainCliOptions;
     }
 
-    @Override
+    /**
+     * CLI options in plugin (main) JVM process.
+     *
+     * @param mainCliOptions options
+     */
     public void setMainCliOptions( List<CommandLineOption> mainCliOptions )
     {
         this.mainCliOptions = mainCliOptions == null ? Collections.<CommandLineOption>emptyList() : mainCliOptions;
@@ -223,7 +234,11 @@ public class BaseProviderFactory
         return skipAfterFailureCount;
     }
 
-    @Override
+    /**
+     * See the plugin configuration parameter "skipAfterFailureCount".
+     *
+     * @param skipAfterFailureCount the value in config parameter "skipAfterFailureCount"
+     */
     public void setSkipAfterFailureCount( int skipAfterFailureCount )
     {
         this.skipAfterFailureCount = skipAfterFailureCount;
@@ -236,18 +251,6 @@ public class BaseProviderFactory
     }
 
     @Override
-    public Shutdown getShutdown()
-    {
-        return shutdown;
-    }
-
-    @Override
-    public void setShutdown( Shutdown shutdown )
-    {
-        this.shutdown = shutdown;
-    }
-
-    @Override
     public Integer getSystemExitTimeout()
     {
         return systemExitTimeout;
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/Command.java b/surefire-api/src/main/java/org/apache/maven/surefire/booter/Command.java
index f05c0f6..834317b 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/Command.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/booter/Command.java
@@ -19,6 +19,8 @@ package org.apache.maven.surefire.booter;
  * under the License.
  */
 
+import java.util.Objects;
+
 import static java.util.Objects.requireNonNull;
 import static org.apache.maven.surefire.shared.utils.StringUtils.isBlank;
 import static org.apache.maven.surefire.booter.MasterProcessCommand.RUN_CLASS;
@@ -47,6 +49,11 @@ public final class Command
         this.data = data;
     }
 
+    public Command( MasterProcessCommand command )
+    {
+        this( command, null );
+    }
+
     public static Command toShutdown( Shutdown shutdownType )
     {
         return new Command( SHUTDOWN, shutdownType.name() );
@@ -57,11 +64,6 @@ public final class Command
         return new Command( RUN_CLASS, runClass );
     }
 
-    public Command( MasterProcessCommand command )
-    {
-        this( command, null );
-    }
-
     public MasterProcessCommand getCommandType()
     {
         return command;
@@ -78,18 +80,13 @@ public final class Command
      */
     public Shutdown toShutdownData()
     {
-        if ( !isType( SHUTDOWN ) )
+        if ( command != SHUTDOWN )
         {
             throw new IllegalStateException( "expected MasterProcessCommand.SHUTDOWN" );
         }
         return isBlank( data ) ? DEFAULT : Shutdown.valueOf( data );
     }
 
-    public boolean isType( MasterProcessCommand command )
-    {
-        return command == this.command;
-    }
-
     @Override
     public boolean equals( Object o )
     {
@@ -105,7 +102,7 @@ public final class Command
 
         Command arg = (Command) o;
 
-        return command == arg.command && ( data == null ? arg.data == null : data.equals( arg.data ) );
+        return command == arg.command && Objects.equals( data, arg.data );
     }
 
     @Override
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/MainCliOptionsAware.java b/surefire-api/src/main/java/org/apache/maven/surefire/booter/MasterProcessChannelEncoder.java
similarity index 60%
copy from surefire-api/src/main/java/org/apache/maven/surefire/booter/MainCliOptionsAware.java
copy to surefire-api/src/main/java/org/apache/maven/surefire/booter/MasterProcessChannelEncoder.java
index eddebed..527782d 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/MainCliOptionsAware.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/booter/MasterProcessChannelEncoder.java
@@ -19,17 +19,31 @@ package org.apache.maven.surefire.booter;
  * under the License.
  */
 
-import org.apache.maven.surefire.cli.CommandLineOption;
-
-import java.util.List;
-
 /**
- * CLI options in plugin (main) JVM process.
+ * magic number : opcode [: opcode specific data]*
+ * <br>
  *
  * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
- * @since 2.19
+ * @since 3.0.0-M4
  */
-interface MainCliOptionsAware
+public final class MasterProcessChannelEncoder
 {
-    void setMainCliOptions( List<CommandLineOption> mainCliOptions );
+
+    private static final String MAGIC_NUMBER = ":maven:surefire:std:out:";
+
+    /**
+     * Encodes opcode and data.
+     *
+     * @param operation opcode
+     * @param data   data
+     * @return encoded command
+     */
+    private static StringBuilder encode( String operation, String data )
+    {
+        StringBuilder s = new StringBuilder( 128 )
+                .append( MAGIC_NUMBER )
+                .append( operation );
+
+        return data == null ? s : s.append( ':' ).append( data );
+    }
 }
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/MasterProcessCommand.java b/surefire-api/src/main/java/org/apache/maven/surefire/booter/MasterProcessCommand.java
index 7c4520f..182eddc 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/MasterProcessCommand.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/booter/MasterProcessCommand.java
@@ -19,44 +19,49 @@ package org.apache.maven.surefire.booter;
  * under the License.
  */
 
-import java.io.DataInputStream;
-import java.io.IOException;
-
 import static java.nio.charset.StandardCharsets.US_ASCII;
 import static java.util.Objects.requireNonNull;
-import static java.lang.String.format;
 
 /**
  * Commands which are sent from plugin to the forked jvm.
  * Support and methods related to the commands.
+ * <br>
+ *     <br>
+ * magic number : opcode [: opcode specific data]*
+ * <br>
+ *     or data encoded with Base64
+ * <br>
+ * magic number : opcode [: Base64(opcode specific data)]*
  *
  * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
  * @since 2.19
  */
 public enum MasterProcessCommand
 {
-    RUN_CLASS( 0, String.class ),
-    TEST_SET_FINISHED( 1, Void.class ),
-    SKIP_SINCE_NEXT_TEST( 2, Void.class ),
-    SHUTDOWN( 3, String.class ),
+    RUN_CLASS( "run-testclass", String.class ),
+    TEST_SET_FINISHED( "testset-finished", Void.class ),
+    SKIP_SINCE_NEXT_TEST( "skip-since-next-test", Void.class ),
+    SHUTDOWN( "shutdown", String.class ),
 
     /** To tell a forked process that the master process is still alive. Repeated after 10 seconds. */
-    NOOP( 4, Void.class ),
-    BYE_ACK( 5, Void.class );
+    NOOP( "noop", Void.class ),
+    BYE_ACK( "bye-ack", Void.class );
+
+    private static final String MAGIC_NUMBER = ":maven-surefire-std-out:";
 
-    private final int id;
+    private final String opcodeName;
 
     private final Class<?> dataType;
 
-    MasterProcessCommand( int id, Class<?> dataType )
+    MasterProcessCommand( String opcodeName, Class<?> dataType )
     {
-        this.id = id;
+        this.opcodeName = opcodeName;
         this.dataType = requireNonNull( dataType, "dataType cannot be null" );
     }
 
-    public int getId()
+    public String getOpcode()
     {
-        return id;
+        return opcodeName;
     }
 
     public Class<?> getDataType()
@@ -69,7 +74,18 @@ public enum MasterProcessCommand
         return dataType != Void.class;
     }
 
-    @SuppressWarnings( "checkstyle:magicnumber" )
+    public static MasterProcessCommand byOpcode( String opcode )
+    {
+        for ( MasterProcessCommand cmd : values() )
+        {
+            if ( cmd.opcodeName.equals( opcode ) )
+            {
+                return cmd;
+            }
+        }
+        return null;
+    }
+
     public byte[] encode( String data )
     {
         if ( !hasDataType() )
@@ -82,109 +98,42 @@ public enum MasterProcessCommand
             throw new IllegalArgumentException( "Data type can be only " + String.class );
         }
 
-        final byte[] dataBytes = fromDataType( data );
-        final int len = dataBytes.length;
-
-        final byte[] encoded = new byte[8 + len];
-
-        final int command = getId();
-        setCommandAndDataLength( command, len, encoded );
-        System.arraycopy( dataBytes, 0, encoded, 8, len );
-
-        return encoded;
+        return encode( opcodeName, data )
+                .toString()
+                .getBytes( US_ASCII );
     }
 
-    @SuppressWarnings( "checkstyle:magicnumber" )
     public byte[] encode()
     {
         if ( getDataType() != Void.class )
         {
             throw new IllegalArgumentException( "Data type can be only " + getDataType() );
         }
-        byte[] encoded = new byte[8];
-        int command = getId();
-        setCommandAndDataLength( command, 0, encoded );
-        return encoded;
-    }
-
-    public static Command decode( DataInputStream is )
-        throws IOException
-    {
-        MasterProcessCommand command = resolve( is.readInt() );
-        if ( command == null )
-        {
-            return null;
-        }
-        else
-        {
-            int dataLength = is.readInt();
-            if ( dataLength > 0 )
-            {
-                byte[] buffer = new byte[ dataLength ];
-                is.readFully( buffer );
 
-                if ( command.getDataType() == Void.class )
-                {
-                    throw new IOException( format( "Command %s unexpectedly read Void data with length %d.",
-                                                   command, dataLength ) );
-                }
-
-                String data = command.toDataTypeAsString( buffer );
-                return new Command( command, data );
-            }
-            else
-            {
-                return new Command( command );
-            }
-        }
+        return encode( opcodeName, null )
+                .toString()
+                .getBytes( US_ASCII );
     }
 
-    String toDataTypeAsString( byte... data )
+    /**
+     * Encodes opcode and data.
+     *
+     * @param operation opcode
+     * @param data   data
+     * @return encoded command
+     */
+    private static StringBuilder encode( String operation, String data )
     {
-        switch ( this )
-        {
-            case RUN_CLASS:
-            case SHUTDOWN:
-                return new String( data, US_ASCII );
-            default:
-                return null;
-        }
-    }
+        StringBuilder s = new StringBuilder( 128 )
+                .append( MAGIC_NUMBER )
+                .append( operation );
 
-    byte[] fromDataType( String data )
-    {
-        switch ( this )
+        if ( data != null )
         {
-            case RUN_CLASS:
-            case SHUTDOWN:
-                return data.getBytes( US_ASCII );
-            default:
-                return new byte[0];
+            s.append( ':' )
+                    .append( data );
         }
-    }
 
-    static MasterProcessCommand resolve( int id )
-    {
-        for ( MasterProcessCommand command : values() )
-        {
-            if ( id == command.id )
-            {
-                return command;
-            }
-        }
-        return null;
-    }
-
-    @SuppressWarnings( "checkstyle:magicnumber" )
-    static void setCommandAndDataLength( int command, int dataLength, byte... encoded )
-    {
-        encoded[0] = (byte) ( command >>> 24 );
-        encoded[1] = (byte) ( command >>> 16 );
-        encoded[2] = (byte) ( command >>> 8 );
-        encoded[3] = (byte) command;
-        encoded[4] = (byte) ( dataLength >>> 24 );
-        encoded[5] = (byte) ( dataLength >>> 16 );
-        encoded[6] = (byte) ( dataLength >>> 8 );
-        encoded[7] = (byte) dataLength;
+        return s.append( ':' );
     }
 }
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/RunOrderParametersAware.java b/surefire-api/src/main/java/org/apache/maven/surefire/booter/RunOrderParametersAware.java
deleted file mode 100644
index 3bee07d..0000000
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/RunOrderParametersAware.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package org.apache.maven.surefire.booter;
-
-/*
- * 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.testset.RunOrderParameters;
-
-/**
- * @author Kristian Rosenvold
- */
-interface RunOrderParametersAware
-{
-    void setRunOrderParameters( RunOrderParameters runOrderParameters );
-}
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/TestArtifactInfoAware.java b/surefire-api/src/main/java/org/apache/maven/surefire/booter/TestArtifactInfoAware.java
deleted file mode 100644
index 9898061..0000000
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/TestArtifactInfoAware.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package org.apache.maven.surefire.booter;
-
-/*
- * 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.testset.TestArtifactInfo;
-
-/**
- * @author Kristian Rosenvold
- */
-interface TestArtifactInfoAware
-{
-    void setTestArtifactInfo( TestArtifactInfo testArtifactInfo );
-}
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/TestRequestAware.java b/surefire-api/src/main/java/org/apache/maven/surefire/booter/TestRequestAware.java
deleted file mode 100644
index 3e98b92..0000000
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/TestRequestAware.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package org.apache.maven.surefire.booter;
-
-/*
- * 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.testset.TestRequest;
-
-/**
- * @author Kristian Rosenvold
- */
-interface TestRequestAware
-{
-    void setTestRequest( TestRequest testSuiteDefinition );
-}
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/ReporterConfigurationAware.java b/surefire-api/src/main/java/org/apache/maven/surefire/providerapi/CommandChainReader.java
similarity index 62%
rename from surefire-api/src/main/java/org/apache/maven/surefire/booter/ReporterConfigurationAware.java
rename to surefire-api/src/main/java/org/apache/maven/surefire/providerapi/CommandChainReader.java
index 8c65be3..2c94e9d 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/ReporterConfigurationAware.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/providerapi/CommandChainReader.java
@@ -1,4 +1,4 @@
-package org.apache.maven.surefire.booter;
+package org.apache.maven.surefire.providerapi;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -19,12 +19,21 @@ package org.apache.maven.surefire.booter;
  * under the License.
  */
 
-import org.apache.maven.surefire.report.ReporterConfiguration;
+import org.apache.maven.surefire.testset.TestSetFailedException;
 
 /**
- * @author Kristian Rosenvold
+ * Hiding CommandReader instance in provider.
  */
-interface ReporterConfigurationAware
+public interface CommandChainReader
 {
-    void setReporterConfiguration( ReporterConfiguration reporterConfiguration );
+    boolean awaitStarted()
+        throws TestSetFailedException;
+
+    void addTestsFinishedListener( CommandListener listener );
+
+    void addSkipNextTestsListener( CommandListener listener );
+
+    void addShutdownListener( CommandListener listener );
+
+    void removeListener( CommandListener listener );
 }
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/CommandListener.java b/surefire-api/src/main/java/org/apache/maven/surefire/providerapi/CommandListener.java
similarity index 90%
rename from surefire-api/src/main/java/org/apache/maven/surefire/booter/CommandListener.java
rename to surefire-api/src/main/java/org/apache/maven/surefire/providerapi/CommandListener.java
index 523ca76..b0d8870 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/CommandListener.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/providerapi/CommandListener.java
@@ -1,4 +1,4 @@
-package org.apache.maven.surefire.booter;
+package org.apache.maven.surefire.providerapi;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -19,6 +19,8 @@ package org.apache.maven.surefire.booter;
  * under the License.
  */
 
+import org.apache.maven.surefire.booter.Command;
+
 /**
  * Command listener interface.
  */
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/providerapi/MasterProcessChannelDecoder.java b/surefire-api/src/main/java/org/apache/maven/surefire/providerapi/MasterProcessChannelDecoder.java
new file mode 100644
index 0000000..6c64b25
--- /dev/null
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/providerapi/MasterProcessChannelDecoder.java
@@ -0,0 +1,47 @@
+package org.apache.maven.surefire.providerapi;
+
+/*
+ * 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.booter.Command;
+
+import java.io.IOException;
+
+/**
+ * An abstraction for physical decoder of commands. The commands are sent from master Maven process and
+ * received by the child forked Surefire process. The session must be open after the MasterProcessChannelDecoderFactory
+ * has created the decoder instance. The session can be closed on the decoder instance.
+ */
+public interface MasterProcessChannelDecoder
+    extends AutoCloseable
+{
+    /**
+     * Reads the bytes from a channel, waiting until the command is read completely or
+     * the channel throws {@link java.io.EOFException}.
+     * <br>
+     * This method is called in a single Thread. The constructor can be called within another thread.
+     *
+     * @return decoded command
+     * @throws IOException exception in channel
+     */
+    Command decode() throws IOException;
+
+    @Override
+    void close() throws IOException;
+}
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/providerapi/ProviderParameters.java b/surefire-api/src/main/java/org/apache/maven/surefire/providerapi/ProviderParameters.java
index 0fea537..e4caae7 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/providerapi/ProviderParameters.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/providerapi/ProviderParameters.java
@@ -20,7 +20,6 @@ package org.apache.maven.surefire.providerapi;
  */
 
 import org.apache.maven.surefire.booter.ForkedChannelEncoder;
-import org.apache.maven.surefire.booter.Shutdown;
 import org.apache.maven.surefire.cli.CommandLineOption;
 import org.apache.maven.surefire.report.ConsoleStream;
 import org.apache.maven.surefire.report.ReporterConfiguration;
@@ -147,9 +146,9 @@ public interface ProviderParameters
      */
     boolean isInsideFork();
 
-    Shutdown getShutdown();
-
     Integer getSystemExitTimeout();
 
     ForkedChannelEncoder getForkedChannelEncoder();
+
+    CommandChainReader getCommandReader();
 }
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/testset/TestListResolver.java b/surefire-api/src/main/java/org/apache/maven/surefire/testset/TestListResolver.java
index c7c123a..e7ed763 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/testset/TestListResolver.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/testset/TestListResolver.java
@@ -41,7 +41,7 @@ import static org.apache.maven.surefire.testset.ResolvedTest.Type.METHOD;
  * composed of included and excluded tests.<br>
  * The methods {@link #shouldRun(String, String)} are filters easily used in JUnit filter or TestNG.
  * This class is independent of JUnit and TestNG API.<br>
- * It is accessed by Java Reflection API in {@link org.apache.maven.surefire.booter.SurefireReflector}
+ * It is accessed by Java Reflection API in {@code org.apache.maven.surefire.booter.SurefireReflector}
  * using specific ClassLoader.
  */
 public class TestListResolver
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/util/ReflectionUtils.java b/surefire-api/src/main/java/org/apache/maven/surefire/util/ReflectionUtils.java
index 57e9ea7..2273842 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/util/ReflectionUtils.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/util/ReflectionUtils.java
@@ -132,25 +132,6 @@ public final class ReflectionUtils
         }
     }
 
-    public static Object instantiateTwoArgs( ClassLoader classLoader, String className, Class<?> param1Class,
-                                             Object param1, Class param2Class, Object param2 )
-    {
-        try
-        {
-            Class<?> aClass = loadClass( classLoader, className );
-            Constructor constructor = getConstructor( aClass, param1Class, param2Class );
-            return constructor.newInstance( param1, param2 );
-        }
-        catch ( InvocationTargetException e )
-        {
-            throw new SurefireReflectionException( e.getTargetException() );
-        }
-        catch ( ReflectiveOperationException e )
-        {
-            throw new SurefireReflectionException( e );
-        }
-    }
-
     public static void invokeSetter( Object o, String name, Class<?> value1clazz, Object value )
     {
         Method setter = getMethod( o, name, value1clazz );
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/DaemonThreadFactory.java b/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/DaemonThreadFactory.java
index 3610a4b..06ddc53 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/DaemonThreadFactory.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/DaemonThreadFactory.java
@@ -19,8 +19,8 @@ package org.apache.maven.surefire.util.internal;
  * under the License.
  */
 
+import java.util.concurrent.Executors;
 import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * Creates new daemon Thread.
@@ -28,29 +28,16 @@ import java.util.concurrent.atomic.AtomicInteger;
 public final class DaemonThreadFactory
     implements ThreadFactory
 {
-    private static final AtomicInteger POOL_NUMBER = new AtomicInteger( 1 );
-
-    private final AtomicInteger threadNumber = new AtomicInteger( 1 );
-
-    private final ThreadGroup group;
-
-    private final String namePrefix;
+    private static final ThreadFactory DEFAULT_THREAD_FACTORY = Executors.defaultThreadFactory();
 
     private DaemonThreadFactory()
     {
-        SecurityManager s = System.getSecurityManager();
-        group = s != null ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
-        namePrefix = "pool-" + POOL_NUMBER.getAndIncrement() + "-thread-";
     }
 
     @Override
     public Thread newThread( Runnable r )
     {
-        Thread t = new Thread( group, r, namePrefix + threadNumber.getAndIncrement() );
-        if ( t.getPriority() != Thread.NORM_PRIORITY )
-        {
-            t.setPriority( Thread.NORM_PRIORITY );
-        }
+        Thread t = DEFAULT_THREAD_FACTORY.newThread( r );
         t.setDaemon( true );
         return t;
     }
@@ -71,34 +58,19 @@ public final class DaemonThreadFactory
 
     public static Thread newDaemonThread( Runnable r )
     {
-        SecurityManager s = System.getSecurityManager();
-        ThreadGroup group = s == null ? Thread.currentThread().getThreadGroup() : s.getThreadGroup();
-        Thread t = new Thread( group, r );
-        if ( t.getPriority() != Thread.NORM_PRIORITY )
-        {
-            t.setPriority( Thread.NORM_PRIORITY );
-        }
-        t.setDaemon( true );
-        return t;
+        return new DaemonThreadFactory().newThread( r );
     }
 
     public static Thread newDaemonThread( Runnable r, String name )
     {
-        SecurityManager s = System.getSecurityManager();
-        ThreadGroup group = s == null ? Thread.currentThread().getThreadGroup() : s.getThreadGroup();
-        Thread t = new Thread( group, r, name );
-        if ( t.getPriority() != Thread.NORM_PRIORITY )
-        {
-            t.setPriority( Thread.NORM_PRIORITY );
-        }
-        t.setDaemon( true );
+        Thread t = new DaemonThreadFactory().newThread( r );
+        t.setName( name );
         return t;
     }
 
     private static class NamedThreadFactory
         implements ThreadFactory
     {
-
         private final String name;
 
         private NamedThreadFactory( String name )
diff --git a/surefire-api/src/test/java/org/apache/maven/JUnit4SuiteTest.java b/surefire-api/src/test/java/org/apache/maven/JUnit4SuiteTest.java
index 38f0c48..ea27d8a 100644
--- a/surefire-api/src/test/java/org/apache/maven/JUnit4SuiteTest.java
+++ b/surefire-api/src/test/java/org/apache/maven/JUnit4SuiteTest.java
@@ -23,11 +23,8 @@ import junit.framework.JUnit4TestAdapter;
 import junit.framework.Test;
 import org.apache.maven.plugin.surefire.runorder.ThreadedExecutionSchedulerTest;
 import org.apache.maven.surefire.SpecificTestClassFilterTest;
-import org.apache.maven.surefire.booter.CommandReaderTest;
 import org.apache.maven.surefire.booter.ForkedChannelEncoderTest;
 import org.apache.maven.surefire.booter.ForkingRunListenerTest;
-import org.apache.maven.surefire.booter.MasterProcessCommandTest;
-import org.apache.maven.surefire.booter.SurefireReflectorTest;
 import org.apache.maven.surefire.report.LegacyPojoStackTraceWriterTest;
 import org.apache.maven.surefire.suite.RunResultTest;
 import org.apache.maven.surefire.testset.FundamentalFilterTest;
@@ -51,11 +48,8 @@ import org.junit.runners.Suite;
  * @since 2.19
  */
 @Suite.SuiteClasses( {
-    CommandReaderTest.class,
     ThreadedExecutionSchedulerTest.class,
     ForkingRunListenerTest.class,
-    MasterProcessCommandTest.class,
-    SurefireReflectorTest.class,
     LegacyPojoStackTraceWriterTest.class,
     RunResultTest.class,
     ResolvedTestTest.class,
diff --git a/surefire-api/src/test/java/org/apache/maven/surefire/booter/MasterProcessCommandTest.java b/surefire-api/src/test/java/org/apache/maven/surefire/booter/MasterProcessCommandTest.java
deleted file mode 100644
index cfd4d5f..0000000
--- a/surefire-api/src/test/java/org/apache/maven/surefire/booter/MasterProcessCommandTest.java
+++ /dev/null
@@ -1,164 +0,0 @@
-package org.apache.maven.surefire.booter;
-
-/*
- * 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 junit.framework.TestCase;
-import org.junit.Rule;
-import org.junit.rules.ExpectedException;
-
-import java.io.ByteArrayInputStream;
-import java.io.DataInputStream;
-import java.io.IOException;
-
-import static org.apache.maven.surefire.booter.MasterProcessCommand.decode;
-import static org.apache.maven.surefire.booter.MasterProcessCommand.resolve;
-import static org.apache.maven.surefire.booter.MasterProcessCommand.setCommandAndDataLength;
-import static org.apache.maven.surefire.booter.MasterProcessCommand.BYE_ACK;
-import static org.apache.maven.surefire.booter.MasterProcessCommand.NOOP;
-import static org.apache.maven.surefire.booter.MasterProcessCommand.RUN_CLASS;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.is;
-
-/**
- * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
- * @since 2.19
- */
-public class MasterProcessCommandTest
-    extends TestCase
-{
-    @Rule
-    public final ExpectedException exception = ExpectedException.none();
-
-    public void testEncodedStreamSequence()
-    {
-        byte[] streamSequence = new byte[10];
-        streamSequence[8] = (byte) 'T';
-        streamSequence[9] = (byte) 'e';
-        setCommandAndDataLength( 256, 2, streamSequence );
-        assertEquals( streamSequence[0], (byte) 0 );
-        assertEquals( streamSequence[1], (byte) 0 );
-        assertEquals( streamSequence[2], (byte) 1 );
-        assertEquals( streamSequence[3], (byte) 0 );
-        assertEquals( streamSequence[4], (byte) 0 );
-        assertEquals( streamSequence[5], (byte) 0 );
-        assertEquals( streamSequence[6], (byte) 0 );
-        assertEquals( streamSequence[7], (byte) 2 );
-        // remain unchanged
-        assertEquals( streamSequence[8], (byte) 'T' );
-        assertEquals( streamSequence[9], (byte) 'e' );
-    }
-
-    public void testResolved()
-    {
-        for ( MasterProcessCommand command : MasterProcessCommand.values() )
-        {
-            assertThat( command, is( resolve( command.getId() ) ) );
-        }
-    }
-
-    public void testDataToByteArrayAndBack()
-    {
-        String dummyData = "pkg.Test";
-        for ( MasterProcessCommand command : MasterProcessCommand.values() )
-        {
-            switch ( command )
-            {
-                case RUN_CLASS:
-                    assertEquals( String.class, command.getDataType() );
-                    byte[] encoded = command.fromDataType( dummyData );
-                    assertThat( encoded.length, is( 8 ) );
-                    assertThat( encoded[0], is( (byte) 'p' ) );
-                    assertThat( encoded[1], is( (byte) 'k' ) );
-                    assertThat( encoded[2], is( (byte) 'g' ) );
-                    assertThat( encoded[3], is( (byte) '.' ) );
-                    assertThat( encoded[4], is( (byte) 'T' ) );
-                    assertThat( encoded[5], is( (byte) 'e' ) );
-                    assertThat( encoded[6], is( (byte) 's' ) );
-                    assertThat( encoded[7], is( (byte) 't' ) );
-                    String decoded = command.toDataTypeAsString( encoded );
-                    assertThat( decoded, is( dummyData ) );
-                    break;
-                case TEST_SET_FINISHED:
-                case SKIP_SINCE_NEXT_TEST:
-                case NOOP:
-                case  BYE_ACK:
-                    assertEquals( Void.class, command.getDataType() );
-                    encoded = command.fromDataType( dummyData );
-                    assertThat( encoded.length, is( 0 ) );
-                    decoded = command.toDataTypeAsString( encoded );
-                    assertNull( decoded );
-                    break;
-                case SHUTDOWN:
-                    assertEquals( String.class, command.getDataType() );
-                    encoded = command.fromDataType( Shutdown.EXIT.name() );
-                    assertThat( encoded.length, is( 4 ) );
-                    decoded = command.toDataTypeAsString( encoded );
-                    assertThat( decoded, is( Shutdown.EXIT.name() ) );
-                    break;
-                default:
-                    fail();
-            }
-            assertThat( command, is( resolve( command.getId() ) ) );
-        }
-    }
-
-    public void testEncodedDecodedIsSameForRunClass()
-        throws IOException
-    {
-        byte[] encoded = RUN_CLASS.encode( "pkg.Test" );
-        assertThat( encoded.length, is( 16 ) );
-        assertThat( encoded[0], is( (byte) 0 ) );
-        assertThat( encoded[1], is( (byte) 0 ) );
-        assertThat( encoded[2], is( (byte) 0 ) );
-        assertThat( encoded[3], is( (byte) 0 ) );
-        assertThat( encoded[4], is( (byte) 0 ) );
-        assertThat( encoded[5], is( (byte) 0 ) );
-        assertThat( encoded[6], is( (byte) 0 ) );
-        assertThat( encoded[7], is( (byte) 8 ) );
-        assertThat( encoded[8], is( (byte) 'p' ) );
-        assertThat( encoded[9], is( (byte) 'k' ) );
-        assertThat( encoded[10], is( (byte) 'g' ) );
-        assertThat( encoded[11], is( (byte) '.' ) );
-        assertThat( encoded[12], is( (byte) 'T' ) );
-        assertThat( encoded[13], is( (byte) 'e' ) );
-        assertThat( encoded[14], is( (byte) 's' ) );
-        assertThat( encoded[15], is( (byte) 't' ) );
-        Command command = decode( new DataInputStream( new ByteArrayInputStream( encoded ) ) );
-        assertNotNull( command );
-        assertThat( command.getCommandType(), is( RUN_CLASS ) );
-        assertThat( command.getData(), is( "pkg.Test" ) );
-    }
-
-    public void testShouldDecodeTwoCommands() throws IOException
-    {
-        byte[] cmd1 = BYE_ACK.encode();
-        byte[] cmd2 = NOOP.encode();
-        byte[] stream = new byte[cmd1.length + cmd2.length];
-        System.arraycopy( cmd1, 0, stream, 0, cmd1.length );
-        System.arraycopy( cmd2, 0, stream, cmd1.length, cmd2.length );
-        DataInputStream is = new DataInputStream( new ByteArrayInputStream( stream ) );
-        Command bye = decode( is );
-        assertNotNull( bye );
-        assertThat( bye.getCommandType(), is( BYE_ACK ) );
-        Command noop = decode( is );
-        assertNotNull( noop );
-        assertThat( noop.getCommandType(), is( NOOP ) );
-    }
-}
diff --git a/surefire-booter/pom.xml b/surefire-booter/pom.xml
index 1fa5045..6ba9af1 100644
--- a/surefire-booter/pom.xml
+++ b/surefire-booter/pom.xml
@@ -36,6 +36,30 @@
       <groupId>org.apache.maven.surefire</groupId>
       <artifactId>surefire-api</artifactId>
       <version>${project.version}</version>
+      <exclusions>
+        <exclusion>
+          <groupId>org.apache.maven.shared</groupId>
+          <artifactId>maven-shared-utils</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven.surefire</groupId>
+      <artifactId>surefire-extensions-spi</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven.shared</groupId>
+      <artifactId>maven-shared-utils</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-lang3</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>commons-io</groupId>
+      <artifactId>commons-io</artifactId>
     </dependency>
     <dependency>
       <groupId>com.google.code.findbugs</groupId>
@@ -73,22 +97,35 @@
   <build>
     <plugins>
       <plugin>
-        <groupId>org.jacoco</groupId>
-        <artifactId>jacoco-maven-plugin</artifactId>
+        <artifactId>maven-dependency-plugin</artifactId>
         <executions>
           <execution>
-            <id>jacoco-instrumentation</id>
+            <id>build-test-classpath</id>
+            <phase>generate-sources</phase>
             <goals>
-              <goal>instrument</goal>
+              <goal>build-classpath</goal>
             </goals>
+            <configuration>
+              <includeScope>test</includeScope>
+              <outputFile>target/test-classpath/cp.txt</outputFile>
+            </configuration>
           </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.jacoco</groupId>
+        <artifactId>jacoco-maven-plugin</artifactId>
+        <executions>
           <execution>
-            <id>restore-classes</id>
+            <id>jacoco-agent</id>
             <goals>
-              <goal>restore-instrumented-classes</goal>
+              <goal>prepare-agent</goal>
             </goals>
           </execution>
         </executions>
+        <configuration>
+          <propertyName>jacoco.agent</propertyName>
+        </configuration>
       </plugin>
       <plugin>
         <artifactId>maven-surefire-plugin</artifactId>
@@ -100,7 +137,7 @@
           </dependency>
         </dependencies>
         <configuration>
-          <argLine>${jvm.args.tests}</argLine>
+          <argLine>${jvm.args.tests} ${jacoco.agent}</argLine>
           <includes>
             <include>**/JUnit4SuiteTest.java</include>
           </includes>
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 fc570f0..fa664be 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
@@ -58,4 +58,5 @@ public final class BooterConstants
     public static final String SYSTEM_EXIT_TIMEOUT = "systemExitTimeout";
     public static final String PLUGIN_PID = "pluginPid";
     public static final String PROCESS_CHECKER = "processChecker";
+    public static final String FORK_NODE_CONNECTION_STRING = "forkNodeConnectionString";
 }
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 b091679..4b31db8 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
@@ -58,6 +58,11 @@ public class BooterDeserializer
         properties = SystemPropertyManager.loadProperties( inputStream );
     }
 
+    public String getForkNodeConnectionString()
+    {
+        return properties.getProperty( FORK_NODE_CONNECTION_STRING );
+    }
+
     /**
      * @return PID of Maven process where plugin is executed; or null if PID could not be determined.
      */
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/CommandReader.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/CommandReader.java
similarity index 83%
rename from surefire-api/src/main/java/org/apache/maven/surefire/booter/CommandReader.java
rename to surefire-booter/src/main/java/org/apache/maven/surefire/booter/CommandReader.java
index b51f713..a197157 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/CommandReader.java
+++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/CommandReader.java
@@ -20,10 +20,13 @@ package org.apache.maven.surefire.booter;
  */
 
 import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
-import org.apache.maven.plugin.surefire.log.api.NullConsoleLogger;
+import org.apache.maven.surefire.booter.spi.MasterProcessCommandNoMagicNumberException;
+import org.apache.maven.surefire.booter.spi.MasterProcessUnknownCommandException;
+import org.apache.maven.surefire.providerapi.CommandChainReader;
+import org.apache.maven.surefire.providerapi.CommandListener;
+import org.apache.maven.surefire.providerapi.MasterProcessChannelDecoder;
 import org.apache.maven.surefire.testset.TestSetFailedException;
 
-import java.io.DataInputStream;
 import java.io.EOFException;
 import java.io.IOException;
 import java.util.Iterator;
@@ -47,7 +50,6 @@ import static org.apache.maven.surefire.booter.MasterProcessCommand.RUN_CLASS;
 import static org.apache.maven.surefire.booter.MasterProcessCommand.SHUTDOWN;
 import static org.apache.maven.surefire.booter.MasterProcessCommand.SKIP_SINCE_NEXT_TEST;
 import static org.apache.maven.surefire.booter.MasterProcessCommand.TEST_SET_FINISHED;
-import static org.apache.maven.surefire.booter.MasterProcessCommand.decode;
 import static org.apache.maven.surefire.util.internal.DaemonThreadFactory.newDaemonThread;
 import static org.apache.maven.surefire.shared.utils.StringUtils.isBlank;
 import static org.apache.maven.surefire.shared.utils.StringUtils.isNotBlank;
@@ -58,12 +60,10 @@ import static org.apache.maven.surefire.shared.utils.StringUtils.isNotBlank;
  * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
  * @since 2.19
  */
-public final class CommandReader
+public final class CommandReader implements CommandChainReader
 {
     private static final String LAST_TEST_SYMBOL = "";
 
-    private static final CommandReader READER = new CommandReader();
-
     private final Queue<BiProperty<MasterProcessCommand, CommandListener>> listeners = new ConcurrentLinkedQueue<>();
 
     private final Thread commandThread = newDaemonThread( new CommandRunnable(), "surefire-forkedjvm-command-thread" );
@@ -76,38 +76,24 @@ public final class CommandReader
 
     private final CopyOnWriteArrayList<String> testClasses = new CopyOnWriteArrayList<>();
 
-    private volatile Shutdown shutdown;
-
-    private int iteratedCount;
-
-    private volatile ConsoleLogger logger = new NullConsoleLogger();
+    private final MasterProcessChannelDecoder decoder;
 
-    private CommandReader()
-    {
-    }
+    private final Shutdown shutdown;
 
-    public static CommandReader getReader()
-    {
-        final CommandReader reader = READER;
-        if ( reader.state.compareAndSet( NEW, RUNNABLE ) )
-        {
-            reader.commandThread.start();
-        }
-        return reader;
-    }
+    private final ConsoleLogger logger;
 
-    public CommandReader setShutdown( Shutdown shutdown )
-    {
-        this.shutdown = shutdown;
-        return this;
-    }
+    private int iteratedCount;
 
-    public CommandReader setLogger( ConsoleLogger logger )
+    public CommandReader( MasterProcessChannelDecoder decoder, Shutdown shutdown, ConsoleLogger logger )
     {
+        this.decoder = requireNonNull( decoder, "null decoder" );
+        this.shutdown = requireNonNull( shutdown, "null Shutdown config" );
         this.logger = requireNonNull( logger, "null logger" );
-        return this;
+        state.set( RUNNABLE );
+        commandThread.start();
     }
 
+    @Override
     public boolean awaitStarted()
         throws TestSetFailedException
     {
@@ -143,11 +129,13 @@ public final class CommandReader
         addListener( RUN_CLASS, listener );
     }
 
+    @Override
     public void addTestsFinishedListener( CommandListener listener )
     {
         addListener( TEST_SET_FINISHED, listener );
     }
 
+    @Override
     public void addSkipNextTestsListener( CommandListener listener )
     {
         addListener( SKIP_SINCE_NEXT_TEST, listener );
@@ -173,6 +161,7 @@ public final class CommandReader
         listeners.add( new BiProperty<>( cmd, listener ) );
     }
 
+    @Override
     public void removeListener( CommandListener listener )
     {
         for ( Iterator<BiProperty<MasterProcessCommand, CommandListener>> it = listeners.iterator(); it.hasNext(); )
@@ -374,54 +363,43 @@ public final class CommandReader
         public void run()
         {
             CommandReader.this.startMonitor.countDown();
-            DataInputStream stdIn = new DataInputStream( System.in );
             boolean isTestSetFinished = false;
             try
             {
                 while ( CommandReader.this.state.get() == RUNNABLE )
                 {
-                    Command command = decode( stdIn );
-                    if ( command == null )
-                    {
-                        String errorMessage = "[SUREFIRE] std/in stream corrupted: first sequence not recognized";
-                        DumpErrorSingleton.getSingleton().dumpStreamText( errorMessage );
-                        logger.error( errorMessage );
-                        break;
-                    }
-                    else
+                    Command command = CommandReader.this.decoder.decode();
+                    switch ( command.getCommandType() )
                     {
-                        switch ( command.getCommandType() )
-                        {
-                            case RUN_CLASS:
-                                String test = command.getData();
-                                boolean inserted = CommandReader.this.insertToQueue( test );
-                                if ( inserted )
-                                {
-                                    CommandReader.this.wakeupIterator();
-                                    callListeners( command );
-                                }
-                                break;
-                            case TEST_SET_FINISHED:
-                                CommandReader.this.makeQueueFull();
-                                isTestSetFinished = true;
-                                CommandReader.this.wakeupIterator();
-                                callListeners( command );
-                                break;
-                            case SHUTDOWN:
-                                CommandReader.this.makeQueueFull();
+                        case RUN_CLASS:
+                            String test = command.getData();
+                            boolean inserted = CommandReader.this.insertToQueue( test );
+                            if ( inserted )
+                            {
                                 CommandReader.this.wakeupIterator();
                                 callListeners( command );
+                            }
+                            break;
+                        case TEST_SET_FINISHED:
+                            CommandReader.this.makeQueueFull();
+                            isTestSetFinished = true;
+                            CommandReader.this.wakeupIterator();
+                            callListeners( command );
+                            break;
+                        case SHUTDOWN:
+                            CommandReader.this.makeQueueFull();
+                            CommandReader.this.wakeupIterator();
+                            callListeners( command );
                                 break;
                             case BYE_ACK:
                                 callListeners( command );
-                                // After SHUTDOWN no more commands can come.
+                            // After SHUTDOWN no more commands can come.
                                 // Hence, do NOT go back to blocking in I/O.
                                 CommandReader.this.state.set( TERMINATED );
                                 break;
-                            default:
-                                callListeners( command );
-                                break;
-                        }
+                        default:
+                            callListeners( command );
+                            break;
                     }
                 }
             }
@@ -439,6 +417,11 @@ public final class CommandReader
                     // does not go to finally for non-default config: Shutdown.EXIT or Shutdown.KILL
                 }
             }
+            catch ( MasterProcessCommandNoMagicNumberException | MasterProcessUnknownCommandException e )
+            {
+                DumpErrorSingleton.getSingleton().dumpStreamException( e );
+                CommandReader.this.logger.error( e );
+            }
             catch ( IOException e )
             {
                 CommandReader.this.state.set( TERMINATED );
@@ -447,7 +430,7 @@ public final class CommandReader
                 {
                     String msg = "[SUREFIRE] std/in stream corrupted";
                     DumpErrorSingleton.getSingleton().dumpStreamException( e, msg );
-                    logger.error( msg, e );
+                    CommandReader.this.logger.error( msg, e );
                 }
             }
             finally
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 2701221..4e0bf99 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
@@ -20,9 +20,14 @@ package org.apache.maven.surefire.booter;
  */
 
 import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
+import org.apache.maven.surefire.booter.spi.DefaultMasterProcessChannelDecoderFactory;
+import org.apache.maven.surefire.providerapi.CommandListener;
+import org.apache.maven.surefire.providerapi.MasterProcessChannelDecoder;
 import org.apache.maven.surefire.providerapi.ProviderParameters;
 import org.apache.maven.surefire.providerapi.SurefireProvider;
 import org.apache.maven.surefire.report.LegacyPojoStackTraceWriter;
+import org.apache.maven.surefire.report.StackTraceWriter;
+import org.apache.maven.surefire.spi.MasterProcessChannelDecoderFactory;
 import org.apache.maven.surefire.testset.TestSetFailedException;
 
 import java.io.File;
@@ -37,6 +42,7 @@ import java.lang.reflect.InvocationTargetException;
 import java.security.AccessControlException;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
+import java.util.ServiceLoader;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ScheduledThreadPoolExecutor;
 import java.util.concurrent.Semaphore;
@@ -73,7 +79,6 @@ public final class ForkedBooter
     private static final String LAST_DITCH_SHUTDOWN_THREAD = "surefire-forkedjvm-last-ditch-daemon-shutdown-thread-";
     private static final String PING_THREAD = "surefire-forkedjvm-ping-";
 
-    private final CommandReader commandReader = CommandReader.getReader();
     private final ForkedChannelEncoder eventChannel = new ForkedChannelEncoder( System.out );
     private final Semaphore exitBarrier = new Semaphore( 0 );
 
@@ -83,6 +88,7 @@ public final class ForkedBooter
     private ScheduledThreadPoolExecutor jvmTerminator;
     private ProviderConfiguration providerConfiguration;
     private ForkingReporterFactory forkingReporterFactory;
+    private volatile CommandReader commandReader;
     private StartupConfiguration startupConfiguration;
     private Object testSet;
 
@@ -109,6 +115,10 @@ public final class ForkedBooter
         forkingReporterFactory = createForkingReporterFactory();
 
         ConsoleLogger logger = (ConsoleLogger) forkingReporterFactory.createReporter();
+        String communicationConfig = startupConfiguration.getInterProcessChannelConfiguration();
+        MasterProcessChannelDecoder decoder = lookupDecoderFactory().createDecoder( communicationConfig, logger );
+        commandReader = new CommandReader( decoder, providerConfiguration.getShutdown(), logger );
+
         pingScheduler = isDebugging() ? null : listenToShutdownCommands( booterDeserializer.getPluginPid(), logger );
 
         systemExitTimeoutInSeconds = providerConfiguration.systemExitTimeout( DEFAULT_SYSTEM_EXIT_TIMEOUT_IN_SECONDS );
@@ -162,7 +172,7 @@ public final class ForkedBooter
         }
         else if ( readTestsFromCommandReader )
         {
-            return new LazyTestsToRun( eventChannel );
+            return new LazyTestsToRun( eventChannel, commandReader );
         }
         return null;
     }
@@ -418,7 +428,9 @@ public final class ForkedBooter
 
     private SurefireProvider createProviderInCurrentClassloader( ForkingReporterFactory reporterManagerFactory )
     {
-        BaseProviderFactory bpf = new BaseProviderFactory( reporterManagerFactory, true );
+        BaseProviderFactory bpf = new BaseProviderFactory( true );
+        bpf.setReporterFactory( reporterManagerFactory );
+        bpf.setCommandReader( commandReader );
         bpf.setTestRequest( providerConfiguration.getTestSuiteDefinition() );
         bpf.setReporterConfiguration( providerConfiguration.getReporterConfiguration() );
         bpf.setForkedChannelEncoder( eventChannel );
@@ -430,12 +442,30 @@ public final class ForkedBooter
         bpf.setDirectoryScannerParameters( providerConfiguration.getDirScannerParams() );
         bpf.setMainCliOptions( providerConfiguration.getMainCliOptions() );
         bpf.setSkipAfterFailureCount( providerConfiguration.getSkipAfterFailureCount() );
-        bpf.setShutdown( providerConfiguration.getShutdown() );
         bpf.setSystemExitTimeout( providerConfiguration.getSystemExitTimeout() );
         String providerClass = startupConfiguration.getActualClassName();
         return (SurefireProvider) instantiateOneArg( classLoader, providerClass, ProviderParameters.class, bpf );
     }
 
+    private static MasterProcessChannelDecoderFactory lookupDecoderFactory()
+    {
+        MasterProcessChannelDecoderFactory defaultDecoderFactory = null;
+        MasterProcessChannelDecoderFactory customDecoderFactory = null;
+        for ( MasterProcessChannelDecoderFactory decoderFactory : ServiceLoader.load(
+                MasterProcessChannelDecoderFactory.class ) )
+        {
+            if ( decoderFactory.getClass() == DefaultMasterProcessChannelDecoderFactory.class )
+            {
+                defaultDecoderFactory = decoderFactory;
+            }
+            else
+            {
+                customDecoderFactory = decoderFactory;
+            }
+        }
+        return defaultDecoderFactory == null ? customDecoderFactory : defaultDecoderFactory;
+    }
+
     /**
      * This method is invoked when Surefire is forked - this method parses and organizes the arguments passed to it and
      * then calls the Surefire class' run method. <br> The system exit code will be 1 if an exception is thrown.
@@ -465,6 +495,8 @@ public final class ForkedBooter
         {
             DumpErrorSingleton.getSingleton().dumpException( t );
             t.printStackTrace();
+            StackTraceWriter stack = new LegacyPojoStackTraceWriter( "test subsystem", "no method", t );
+            booter.eventChannel.consoleErrorLog( stack, false );
             booter.cancelPingScheduler();
             booter.exit1();
         }
diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/LazyTestsToRun.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/LazyTestsToRun.java
index 9d0b2e0..ada3384 100644
--- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/LazyTestsToRun.java
+++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/LazyTestsToRun.java
@@ -25,7 +25,6 @@ import java.util.Iterator;
 import org.apache.maven.surefire.util.CloseableIterator;
 import org.apache.maven.surefire.util.TestsToRun;
 
-import static org.apache.maven.surefire.booter.CommandReader.getReader;
 import static org.apache.maven.surefire.util.ReflectionUtils.loadClass;
 
 /**
@@ -44,23 +43,24 @@ final class LazyTestsToRun
     extends TestsToRun
 {
     private final ForkedChannelEncoder eventChannel;
+    private final CommandReader commandReader;
 
     /**
      * C'tor
      *
      * @param eventChannel the output stream to use when requesting new new tests
      */
-    LazyTestsToRun( ForkedChannelEncoder eventChannel )
+    LazyTestsToRun( ForkedChannelEncoder eventChannel, CommandReader commandReader )
     {
         super( Collections.<Class<?>>emptySet() );
-
         this.eventChannel = eventChannel;
+        this.commandReader = commandReader;
     }
 
     private final class BlockingIterator
         implements Iterator<Class<?>>
     {
-        private final Iterator<String> it = getReader().getIterableClasses( eventChannel ).iterator();
+        private final Iterator<String> it = commandReader.getIterableClasses( eventChannel ).iterator();
 
         @Override
         public boolean hasNext()
@@ -132,7 +132,7 @@ final class LazyTestsToRun
      */
     private Iterator<Class<?>> newWeakIterator()
     {
-        final Iterator<String> it = getReader().iterated();
+        final Iterator<String> it = commandReader.iterated();
         return new CloseableIterator<Class<?>>()
         {
             @Override
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 c7379e4..caa26d2 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
@@ -96,7 +96,6 @@ public class ProviderConfiguration
         return reporterConfiguration;
     }
 
-
     public boolean isFailIfNoTests()
     {
         return failIfNoTests;
@@ -107,7 +106,6 @@ public class ProviderConfiguration
         return dirScannerParams.getTestClassesDirectory();
     }
 
-
     public DirectoryScannerParameters getDirScannerParams()
     {
         return dirScannerParams;
diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProviderFactory.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProviderFactory.java
index 41badfd..614ff92 100644
--- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProviderFactory.java
+++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProviderFactory.java
@@ -99,7 +99,8 @@ public class ProviderFactory
         final ClassLoader systemClassLoader = currentThread.getContextClassLoader();
         currentThread.setContextClassLoader( classLoader );
         // Note: Duplicated in ForkedBooter#createProviderInCurrentClassloader
-        Object o = surefireReflector.createBooterConfiguration( classLoader, reporterManagerFactory, isInsideFork );
+        Object o = surefireReflector.createBooterConfiguration( classLoader, isInsideFork );
+        surefireReflector.setReporterFactoryAware( o, reporterManagerFactory );
         surefireReflector.setTestSuiteDefinitionAware( o, providerConfiguration.getTestSuiteDefinition() );
         surefireReflector.setProviderPropertiesAware( o, providerConfiguration.getProviderProperties() );
         surefireReflector.setReporterConfigurationAware( o, providerConfiguration.getReporterConfiguration() );
@@ -109,7 +110,6 @@ public class ProviderFactory
         surefireReflector.setIfDirScannerAware( o, providerConfiguration.getDirScannerParams() );
         surefireReflector.setMainCliOptions( o, providerConfiguration.getMainCliOptions() );
         surefireReflector.setSkipAfterFailureCount( o, providerConfiguration.getSkipAfterFailureCount() );
-        surefireReflector.setShutdown( o, providerConfiguration.getShutdown() );
         if ( isInsideFork )
         {
             surefireReflector.setSystemExitTimeout( o, providerConfiguration.getSystemExitTimeout() );
diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/StartupConfiguration.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/StartupConfiguration.java
index 0bbe988..44eb5b7 100644
--- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/StartupConfiguration.java
+++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/StartupConfiguration.java
@@ -50,6 +50,11 @@ public class StartupConfiguration
         this.processChecker = processChecker;
     }
 
+    public String getInterProcessChannelConfiguration()
+    {
+        return "pipe:std:in";
+    }
+
     public boolean isProviderMainClass()
     {
         return providerClassName.endsWith( "#main" );
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/SurefireReflector.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/SurefireReflector.java
similarity index 81%
rename from surefire-api/src/main/java/org/apache/maven/surefire/booter/SurefireReflector.java
rename to surefire-booter/src/main/java/org/apache/maven/surefire/booter/SurefireReflector.java
index 5cc1415..20ff510 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/SurefireReflector.java
+++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/SurefireReflector.java
@@ -19,8 +19,6 @@ package org.apache.maven.surefire.booter;
  * under the License.
  */
 
-import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
-import org.apache.maven.plugin.surefire.log.api.ConsoleLoggerDecorator;
 import org.apache.maven.surefire.cli.CommandLineOption;
 import org.apache.maven.surefire.providerapi.ProviderParameters;
 import org.apache.maven.surefire.report.ReporterConfiguration;
@@ -46,7 +44,6 @@ import static java.util.Collections.checkedList;
 import static org.apache.maven.surefire.util.ReflectionUtils.getConstructor;
 import static org.apache.maven.surefire.util.ReflectionUtils.getMethod;
 import static org.apache.maven.surefire.util.ReflectionUtils.instantiateOneArg;
-import static org.apache.maven.surefire.util.ReflectionUtils.instantiateTwoArgs;
 import static org.apache.maven.surefire.util.ReflectionUtils.invokeGetter;
 import static org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray;
 import static org.apache.maven.surefire.util.ReflectionUtils.invokeSetter;
@@ -59,7 +56,7 @@ import static org.apache.maven.surefire.util.ReflectionUtils.newInstance;
  *
  * @author Kristian Rosenvold
  */
-public class SurefireReflector
+public final class SurefireReflector
 {
     private final ClassLoader surefireClassLoader;
 
@@ -69,21 +66,11 @@ public class SurefireReflector
 
     private final Class<?> testArtifactInfo;
 
-    private final Class<?> testArtifactInfoAware;
-
     private final Class<?> directoryScannerParameters;
 
     private final Class<?> runOrderParameters;
 
-    private final Class<?> directoryScannerParametersAware;
-
-    private final Class<?> testSuiteDefinitionAware;
-
-    private final Class<?> testClassLoaderAware;
-
-    private final Class<?> reporterConfigurationAware;
-
-    private final Class<?> providerPropertiesAware;
+    private final Class<?> baseProviderFactory;
 
     private final Class<?> runResult;
 
@@ -93,12 +80,8 @@ public class SurefireReflector
 
     private final Class<?> testListResolver;
 
-    private final Class<?> mainCliOptions;
-
     private final Class<Enum> commandLineOptionsClass;
 
-    private final Class<?> shutdownAwareClass;
-
     private final Class<Enum> shutdownClass;
 
     @SuppressWarnings( "unchecked" )
@@ -110,22 +93,14 @@ public class SurefireReflector
             reporterConfiguration = surefireClassLoader.loadClass( ReporterConfiguration.class.getName() );
             testRequest = surefireClassLoader.loadClass( TestRequest.class.getName() );
             testArtifactInfo = surefireClassLoader.loadClass( TestArtifactInfo.class.getName() );
-            testArtifactInfoAware = surefireClassLoader.loadClass( TestArtifactInfoAware.class.getName() );
             directoryScannerParameters = surefireClassLoader.loadClass( DirectoryScannerParameters.class.getName() );
             runOrderParameters = surefireClassLoader.loadClass( RunOrderParameters.class.getName() );
-            directoryScannerParametersAware =
-                surefireClassLoader.loadClass( DirectoryScannerParametersAware.class.getName() );
-            testSuiteDefinitionAware = surefireClassLoader.loadClass( TestRequestAware.class.getName() );
-            testClassLoaderAware = surefireClassLoader.loadClass( SurefireClassLoadersAware.class.getName() );
-            reporterConfigurationAware = surefireClassLoader.loadClass( ReporterConfigurationAware.class.getName() );
-            providerPropertiesAware = surefireClassLoader.loadClass( ProviderPropertiesAware.class.getName() );
+            baseProviderFactory = surefireClassLoader.loadClass( BaseProviderFactory.class.getName() );
             reporterFactory = surefireClassLoader.loadClass( ReporterFactory.class.getName() );
             runResult = surefireClassLoader.loadClass( RunResult.class.getName() );
             booterParameters = surefireClassLoader.loadClass( ProviderParameters.class.getName() );
             testListResolver = surefireClassLoader.loadClass( TestListResolver.class.getName() );
-            mainCliOptions = surefireClassLoader.loadClass( MainCliOptionsAware.class.getName() );
             commandLineOptionsClass = (Class<Enum>) surefireClassLoader.loadClass( CommandLineOption.class.getName() );
-            shutdownAwareClass = surefireClassLoader.loadClass( ShutdownAware.class.getName() );
             shutdownClass = (Class<Enum>) surefireClassLoader.loadClass( Shutdown.class.getName() );
         }
         catch ( ClassNotFoundException e )
@@ -227,11 +202,9 @@ public class SurefireReflector
         return newInstance( constructor, reporterConfig.getReportsDirectory(), reporterConfig.isTrimStackTrace() );
     }
 
-    public Object createBooterConfiguration( ClassLoader surefireClassLoader, Object factoryInstance,
-                                             boolean insideFork )
+    public Object createBooterConfiguration( ClassLoader surefireClassLoader, boolean insideFork )
     {
-        return instantiateTwoArgs( surefireClassLoader, BaseProviderFactory.class.getName(),
-                                   reporterFactory, factoryInstance, boolean.class, insideFork );
+        return instantiateOneArg( surefireClassLoader, BaseProviderFactory.class.getName(), boolean.class, insideFork );
     }
 
     public Object instantiateProvider( String providerClassName, Object booterParameters )
@@ -241,7 +214,7 @@ public class SurefireReflector
 
     public void setIfDirScannerAware( Object o, DirectoryScannerParameters dirScannerParams )
     {
-        if ( directoryScannerParametersAware.isAssignableFrom( o.getClass() ) )
+        if ( baseProviderFactory.isAssignableFrom( o.getClass() ) )
         {
             setDirectoryScannerParameters( o, dirScannerParams );
         }
@@ -249,7 +222,7 @@ public class SurefireReflector
 
     public void setMainCliOptions( Object o, List<CommandLineOption> options )
     {
-        if ( mainCliOptions.isAssignableFrom( o.getClass() ) )
+        if ( baseProviderFactory.isAssignableFrom( o.getClass() ) )
         {
             List<Enum> newOptions = checkedList( new ArrayList<Enum>( options.size() ), commandLineOptionsClass );
             Collection<Integer> ordinals = toOrdinals( options );
@@ -271,15 +244,12 @@ public class SurefireReflector
 
     public void setShutdown( Object o, Shutdown shutdown )
     {
-        if ( shutdownAwareClass.isAssignableFrom( o.getClass() ) )
+        for ( Enum e : shutdownClass.getEnumConstants() )
         {
-            for ( Enum e : shutdownClass.getEnumConstants() )
+            if ( shutdown.ordinal() == e.ordinal() )
             {
-                if ( shutdown.ordinal() == e.ordinal() )
-                {
-                    invokeSetter( o, "setShutdown", shutdownClass, e );
-                    break;
-                }
+                invokeSetter( o, "setShutdown", shutdownClass, e );
+                break;
             }
         }
     }
@@ -303,7 +273,7 @@ public class SurefireReflector
 
     public void setTestSuiteDefinitionAware( Object o, TestRequest testSuiteDefinition2 )
     {
-        if ( testSuiteDefinitionAware.isAssignableFrom( o.getClass() ) )
+        if ( baseProviderFactory.isAssignableFrom( o.getClass() ) )
         {
             setTestSuiteDefinition( o, testSuiteDefinition2 );
         }
@@ -317,7 +287,7 @@ public class SurefireReflector
 
     public void setProviderPropertiesAware( Object o, Map<String, String> properties )
     {
-        if ( providerPropertiesAware.isAssignableFrom( o.getClass() ) )
+        if ( baseProviderFactory.isAssignableFrom( o.getClass() ) )
         {
             setProviderProperties( o, properties );
         }
@@ -330,7 +300,7 @@ public class SurefireReflector
 
     public void setReporterConfigurationAware( Object o, ReporterConfiguration reporterConfiguration1 )
     {
-        if ( reporterConfigurationAware.isAssignableFrom( o.getClass() ) )
+        if ( baseProviderFactory.isAssignableFrom( o.getClass() ) )
         {
             setReporterConfiguration( o, reporterConfiguration1 );
         }
@@ -344,7 +314,7 @@ public class SurefireReflector
 
     public void setTestClassLoaderAware( Object o, ClassLoader testClassLoader )
     {
-        if ( testClassLoaderAware.isAssignableFrom( o.getClass() ) )
+        if ( baseProviderFactory.isAssignableFrom( o.getClass() ) )
         {
             setTestClassLoader( o, testClassLoader );
         }
@@ -358,7 +328,7 @@ public class SurefireReflector
 
     public void setTestArtifactInfoAware( Object o, TestArtifactInfo testArtifactInfo1 )
     {
-        if ( testArtifactInfoAware.isAssignableFrom( o.getClass() ) )
+        if ( baseProviderFactory.isAssignableFrom( o.getClass() ) )
         {
             setTestArtifactInfo( o, testArtifactInfo1 );
         }
@@ -370,6 +340,19 @@ public class SurefireReflector
         invokeSetter( o, "setTestArtifactInfo", this.testArtifactInfo, param );
     }
 
+    public void setReporterFactoryAware( Object o, Object reporterFactory )
+    {
+        if ( baseProviderFactory.isAssignableFrom( o.getClass() ) )
+        {
+            setReporterFactory( o, reporterFactory );
+        }
+    }
+
+    void setReporterFactory( Object o, Object reporterFactory )
+    {
+        invokeSetter( o, "setReporterFactory", this.reporterFactory, reporterFactory );
+    }
+
     private boolean isRunResult( Object o )
     {
         return runResult.isAssignableFrom( o.getClass() );
@@ -384,17 +367,4 @@ public class SurefireReflector
         }
         return ordinals;
     }
-
-    public static Object createConsoleLogger( ConsoleLogger consoleLogger, ClassLoader cl )
-    {
-        try
-        {
-            Class<?> decoratorClass = cl.loadClass( ConsoleLoggerDecorator.class.getName() );
-            return getConstructor( decoratorClass, Object.class ).newInstance( consoleLogger );
-        }
-        catch ( Exception e )
-        {
-            throw new SurefireReflectionException( e );
-        }
-    }
 }
diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/DefaultMasterProcessChannelDecoder.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/DefaultMasterProcessChannelDecoder.java
new file mode 100644
index 0000000..4b363ee
--- /dev/null
+++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/DefaultMasterProcessChannelDecoder.java
@@ -0,0 +1,162 @@
+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.plugin.surefire.log.api.ConsoleLogger;
+import org.apache.maven.surefire.booter.Command;
+import org.apache.maven.surefire.booter.MasterProcessCommand;
+import org.apache.maven.surefire.providerapi.MasterProcessChannelDecoder;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * magic number : opcode [: opcode specific data]*
+ * <br>
+ *
+ * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 3.0.0-M4
+ */
+public class DefaultMasterProcessChannelDecoder implements MasterProcessChannelDecoder
+{
+    private final InputStream is;
+    private final ConsoleLogger logger;
+
+    public DefaultMasterProcessChannelDecoder( InputStream is, ConsoleLogger logger )
+    {
+        this.is = is;
+        this.logger = logger;
+    }
+
+    protected boolean hasData( String opcode )
+    {
+        MasterProcessCommand cmd = MasterProcessCommand.byOpcode( opcode );
+        return cmd == null || cmd.hasDataType();
+    }
+
+    @SuppressWarnings( "checkstyle:innerassignment" )
+    @Override
+    public Command decode() throws IOException
+    {
+        List<String> tokens = new ArrayList<>();
+        StringBuilder frame = new StringBuilder();
+        boolean frameStarted = false;
+        boolean frameFinished = false;
+        for ( int r; ( r = is.read() ) != -1 ; )
+        {
+            char c = (char) r;
+            if ( frameFinished && c == '\n' )
+            {
+                continue;
+            }
+
+            if ( !frameStarted )
+            {
+                if ( c == ':' )
+                {
+                    frameStarted = true;
+                    frameFinished = false;
+                    frame.setLength( 0 );
+                    tokens.clear();
+                    continue;
+                }
+            }
+            else if ( !frameFinished )
+            {
+                boolean isColon = c == ':';
+                if ( isColon || c == '\n' || c == '\r' )
+                {
+                    tokens.add( frame.toString() );
+                    frame.setLength( 0 );
+                }
+                else
+                {
+                    frame.append( c );
+                }
+                boolean isFinishedFrame = isTokenComplete( tokens );
+                if ( isFinishedFrame )
+                {
+                    frameFinished = true;
+                    frameStarted = false;
+                    break;
+                }
+            }
+
+            boolean removed = removeUnsynchronizedTokens( tokens, logger );
+            if ( removed && tokens.isEmpty() )
+            {
+                frameStarted = false;
+                frameFinished = true;
+            }
+        }
+
+        if ( tokens.size() <= 1 )
+        {
+            throw new MasterProcessCommandNoMagicNumberException( frame.toString() );
+        }
+        if ( tokens.size() == 2 )
+        {
+            return new Command( MasterProcessCommand.byOpcode( tokens.get( 1 ) ) );
+        }
+        else if ( tokens.size() == 3 )
+        {
+            return new Command( MasterProcessCommand.byOpcode( tokens.get( 1 ) ), tokens.get( 2 ) );
+        }
+        else
+        {
+            throw new MasterProcessUnknownCommandException( frame.toString() );
+        }
+    }
+
+    private boolean isTokenComplete( List<String> tokens )
+    {
+        if ( tokens.size() >= 2 )
+        {
+            return hasData( tokens.get( 1 ) ) == ( tokens.size() == 3 );
+        }
+        return false;
+    }
+
+    private boolean removeUnsynchronizedTokens( Collection<String> tokens, ConsoleLogger logger )
+    {
+        boolean removed = false;
+        for ( Iterator<String> it = tokens.iterator(); it.hasNext(); )
+        {
+            String token = it.next();
+            if ( token.equals( "maven-surefire-std-out" ) )
+            {
+                break;
+            }
+            removed = true;
+            it.remove();
+            logger.error( "Forked JVM could not synchronize the '" + token + "' token with preamble sequence." );
+        }
+        return removed;
+    }
+
+    @Override
+    public void close()
+    {
+    }
+}
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/MainCliOptionsAware.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/DefaultMasterProcessChannelDecoderFactory.java
similarity index 50%
copy from surefire-api/src/main/java/org/apache/maven/surefire/booter/MainCliOptionsAware.java
copy to surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/DefaultMasterProcessChannelDecoderFactory.java
index eddebed..f7e7911 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/MainCliOptionsAware.java
+++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/DefaultMasterProcessChannelDecoderFactory.java
@@ -1,4 +1,4 @@
-package org.apache.maven.surefire.booter;
+package org.apache.maven.surefire.booter.spi;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -19,17 +19,28 @@ package org.apache.maven.surefire.booter;
  * under the License.
  */
 
-import org.apache.maven.surefire.cli.CommandLineOption;
+import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
+import org.apache.maven.surefire.providerapi.MasterProcessChannelDecoder;
+import org.apache.maven.surefire.spi.MasterProcessChannelDecoderFactory;
 
-import java.util.List;
+import java.io.IOException;
 
 /**
- * CLI options in plugin (main) JVM process.
  *
- * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
- * @since 2.19
  */
-interface MainCliOptionsAware
+public class DefaultMasterProcessChannelDecoderFactory
+        implements MasterProcessChannelDecoderFactory
 {
-    void setMainCliOptions( List<CommandLineOption> mainCliOptions );
+    @Override
+    public MasterProcessChannelDecoder createDecoder( String channelConfig, ConsoleLogger logger ) throws IOException
+    {
+        if ( "pipe:std:in".equals( channelConfig ) )
+        {
+            return new DefaultMasterProcessChannelDecoder( System.in, logger );
+        }
+        else
+        {
+            throw new IOException( "Unknown chanel configuration string " + channelConfig );
+        }
+    }
 }
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/MainCliOptionsAware.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/MasterProcessCommandNoMagicNumberException.java
similarity index 65%
copy from surefire-api/src/main/java/org/apache/maven/surefire/booter/MainCliOptionsAware.java
copy to surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/MasterProcessCommandNoMagicNumberException.java
index eddebed..261969e 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/MainCliOptionsAware.java
+++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/MasterProcessCommandNoMagicNumberException.java
@@ -1,4 +1,4 @@
-package org.apache.maven.surefire.booter;
+package org.apache.maven.surefire.booter.spi;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -19,17 +19,20 @@ package org.apache.maven.surefire.booter;
  * under the License.
  */
 
-import org.apache.maven.surefire.cli.CommandLineOption;
+import org.apache.maven.surefire.booter.MasterProcessCommand;
 
-import java.util.List;
+import java.io.IOException;
 
 /**
- * CLI options in plugin (main) JVM process.
+ * No magic number recognized in the command line, see the JavaDoc in {@link MasterProcessCommand}.
  *
  * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
- * @since 2.19
+ * @since 3.0.0-M4
  */
-interface MainCliOptionsAware
+public class MasterProcessCommandNoMagicNumberException extends IOException
 {
-    void setMainCliOptions( List<CommandLineOption> mainCliOptions );
+    MasterProcessCommandNoMagicNumberException( String line )
+    {
+        super( "No magic # recognized in the line '" + line + "'" );
+    }
 }
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/MainCliOptionsAware.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/MasterProcessUnknownCommandException.java
similarity index 63%
copy from surefire-api/src/main/java/org/apache/maven/surefire/booter/MainCliOptionsAware.java
copy to surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/MasterProcessUnknownCommandException.java
index eddebed..11cab97 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/MainCliOptionsAware.java
+++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/MasterProcessUnknownCommandException.java
@@ -1,4 +1,4 @@
-package org.apache.maven.surefire.booter;
+package org.apache.maven.surefire.booter.spi;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -19,17 +19,21 @@ package org.apache.maven.surefire.booter;
  * under the License.
  */
 
-import org.apache.maven.surefire.cli.CommandLineOption;
+import org.apache.maven.surefire.booter.MasterProcessCommand;
 
-import java.util.List;
+import java.io.IOException;
 
 /**
- * CLI options in plugin (main) JVM process.
+ * No {@link MasterProcessCommand command} recognized according to the opcode
+ * encapsulated in the command line, see the JavaDoc in {@link MasterProcessCommand}.
  *
  * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
- * @since 2.19
+ * @since 3.0.0-M4
  */
-interface MainCliOptionsAware
+public class MasterProcessUnknownCommandException extends IOException
 {
-    void setMainCliOptions( List<CommandLineOption> mainCliOptions );
+    MasterProcessUnknownCommandException( String line )
+    {
+        super( "Unrecognized command found '" + line + "'" );
+    }
 }
diff --git a/surefire-booter/src/main/resources/META-INF/services/org.apache.maven.surefire.spi.MasterProcessChannelDecoderFactory b/surefire-booter/src/main/resources/META-INF/services/org.apache.maven.surefire.spi.MasterProcessChannelDecoderFactory
new file mode 100644
index 0000000..2b44ee1
--- /dev/null
+++ b/surefire-booter/src/main/resources/META-INF/services/org.apache.maven.surefire.spi.MasterProcessChannelDecoderFactory
@@ -0,0 +1,19 @@
+#
+# 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.
+#
+org.apache.maven.surefire.booter.spi.DefaultMasterProcessChannelDecoderFactory
diff --git a/surefire-api/src/test/java/org/apache/maven/surefire/booter/CommandReaderTest.java b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/CommandReaderTest.java
similarity index 86%
rename from surefire-api/src/test/java/org/apache/maven/surefire/booter/CommandReaderTest.java
rename to surefire-booter/src/test/java/org/apache/maven/surefire/booter/CommandReaderTest.java
index 40c0243..096cfb4 100644
--- a/surefire-api/src/test/java/org/apache/maven/surefire/booter/CommandReaderTest.java
+++ b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/CommandReaderTest.java
@@ -19,6 +19,10 @@ package org.apache.maven.surefire.booter;
  * under the License.
  */
 
+import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
+import org.apache.maven.plugin.surefire.log.api.NullConsoleLogger;
+import org.apache.maven.surefire.booter.spi.DefaultMasterProcessChannelDecoder;
+import org.apache.maven.surefire.providerapi.MasterProcessChannelDecoder;
 import org.apache.maven.surefire.testset.TestSetFailedException;
 import org.junit.After;
 import org.junit.Before;
@@ -29,7 +33,6 @@ import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.PrintStream;
-import java.nio.ByteBuffer;
 import java.util.Iterator;
 import java.util.NoSuchElementException;
 import java.util.concurrent.BlockingQueue;
@@ -39,8 +42,7 @@ import java.util.concurrent.FutureTask;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 
-import static java.nio.charset.StandardCharsets.ISO_8859_1;
-
+import static java.nio.charset.StandardCharsets.US_ASCII;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
@@ -61,7 +63,6 @@ public class CommandReaderTest
     private static final long TEST_TIMEOUT = 15_000L;
 
     private final BlockingQueue<Byte> blockingStream = new LinkedBlockingQueue<>();
-    private InputStream realInputStream;
     private CommandReader reader;
 
     static class A
@@ -83,18 +84,19 @@ public class CommandReaderTest
     @Before
     public void init()
     {
+        //noinspection ResultOfMethodCallIgnored
         Thread.interrupted();
-        realInputStream = System.in;
+        InputStream realInputStream = new SystemInputStream();
         addTestToPipeline( getClass().getName() );
-        System.setIn( new SystemInputStream() );
-        reader = CommandReader.getReader();
+        ConsoleLogger logger = new NullConsoleLogger();
+        MasterProcessChannelDecoder decoder = new DefaultMasterProcessChannelDecoder( realInputStream, logger );
+        reader = new CommandReader( decoder, Shutdown.DEFAULT, logger );
     }
 
     @After
     public void deinit()
     {
         reader.stop();
-        System.setIn( realInputStream );
     }
 
     @Test
@@ -242,24 +244,20 @@ public class CommandReaderTest
 
     private void addTestToPipeline( String cls )
     {
-        byte[] clazz = cls.getBytes( ISO_8859_1 );
-        ByteBuffer buffer = ByteBuffer.allocate( 8 + clazz.length ).putInt(
-                MasterProcessCommand.RUN_CLASS.getId() ).putInt( clazz.length ).put( clazz );
-        buffer.rewind();
-        for ( ; buffer.hasRemaining(); )
+        String cmd = ":maven-surefire-std-out:"
+            + MasterProcessCommand.RUN_CLASS.getOpcode() + ':' + cls + '\n';
+        for ( byte cmdByte : cmd.getBytes( US_ASCII ) )
         {
-            blockingStream.add( buffer.get() );
+            blockingStream.add( cmdByte );
         }
     }
 
     private void addEndOfPipeline()
     {
-        ByteBuffer buffer = ByteBuffer.allocate( 8 ).putInt( MasterProcessCommand.TEST_SET_FINISHED.getId() ).putInt(
-                0 );
-        buffer.rewind();
-        for ( ; buffer.hasRemaining(); )
+        String cmd = ":maven-surefire-std-out:" + MasterProcessCommand.TEST_SET_FINISHED.getOpcode() + '\n';
+        for ( byte cmdByte : cmd.getBytes( US_ASCII ) )
         {
-            blockingStream.add( buffer.get() );
+            blockingStream.add( cmdByte );
         }
     }
 
diff --git a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/DefaultMasterProcessChannelDecoderTest.java b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/DefaultMasterProcessChannelDecoderTest.java
new file mode 100644
index 0000000..debab05
--- /dev/null
+++ b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/DefaultMasterProcessChannelDecoderTest.java
@@ -0,0 +1,148 @@
+package org.apache.maven.surefire.booter;
+
+/*
+ * 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 junit.framework.TestCase;
+import org.apache.maven.surefire.booter.spi.DefaultMasterProcessChannelDecoder;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import static org.apache.maven.surefire.booter.MasterProcessCommand.BYE_ACK;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.fest.assertions.Assertions.assertThat;
+
+/**
+ * Tests for {@link DefaultMasterProcessChannelDecoder}.
+ */
+public class DefaultMasterProcessChannelDecoderTest
+    extends TestCase
+{
+    public void testDataToByteArrayAndBack() throws IOException
+    {
+        for ( MasterProcessCommand commandType : MasterProcessCommand.values() )
+        {
+            switch ( commandType )
+            {
+                case RUN_CLASS:
+                    assertEquals( String.class, commandType.getDataType() );
+                    byte[] encoded = commandType.encode( "pkg.Test" );
+                    assertThat( new String( encoded ) )
+                            .isEqualTo( ":maven-surefire-std-out:run-testclass:pkg.Test:" );
+                    byte[] line = addNL( encoded, '\n' );
+                    InputStream is = new ByteArrayInputStream( line );
+                    DefaultMasterProcessChannelDecoder decoder = new DefaultMasterProcessChannelDecoder( is, null );
+                    Command command = decoder.decode();
+                    assertThat( command.getCommandType(), is( commandType ) );
+                    assertThat( command.getData(), is( "pkg.Test" ) );
+                    break;
+                case TEST_SET_FINISHED:
+                    assertThat( commandType ).isSameAs( Command.TEST_SET_FINISHED.getCommandType() );
+                    assertEquals( Void.class, commandType.getDataType() );
+                    encoded = commandType.encode();
+                    assertThat( new String( encoded ) )
+                            .isEqualTo( ":maven-surefire-std-out:testset-finished:" );
+                    is = new ByteArrayInputStream( encoded );
+                    decoder = new DefaultMasterProcessChannelDecoder( is, null );
+                    command = decoder.decode();
+                    assertThat( command.getCommandType(), is( commandType ) );
+                    assertNull( command.getData() );
+                    break;
+                case SKIP_SINCE_NEXT_TEST:
+                    assertThat( commandType ).isSameAs( Command.SKIP_SINCE_NEXT_TEST.getCommandType() );
+                    assertEquals( Void.class, commandType.getDataType() );
+                    encoded = commandType.encode();
+                    assertThat( new String( encoded ) )
+                            .isEqualTo( ":maven-surefire-std-out:skip-since-next-test:" );
+                    is = new ByteArrayInputStream( encoded );
+                    decoder = new DefaultMasterProcessChannelDecoder( is, null );
+                    command = decoder.decode();
+                    assertThat( command.getCommandType(), is( commandType ) );
+                    assertNull( command.getData() );
+                    break;
+                case SHUTDOWN:
+                    assertEquals( String.class, commandType.getDataType() );
+                    encoded = commandType.encode( Shutdown.EXIT.name() );
+                    assertThat( new String( encoded ) )
+                            .isEqualTo( ":maven-surefire-std-out:shutdown:EXIT:" );
+                    is = new ByteArrayInputStream( encoded );
+                    decoder = new DefaultMasterProcessChannelDecoder( is, null );
+                    command = decoder.decode();
+                    assertThat( command.getCommandType(), is( commandType ) );
+                    assertThat( command.getData(), is( "EXIT" ) );
+                    break;
+                case NOOP:
+                    assertThat( commandType ).isSameAs( Command.NOOP.getCommandType() );
+                    assertEquals( Void.class, commandType.getDataType() );
+                    encoded = commandType.encode();
+                    assertThat( new String( encoded ) )
+                            .isEqualTo( ":maven-surefire-std-out:noop:" );
+                    is = new ByteArrayInputStream( encoded );
+                    decoder = new DefaultMasterProcessChannelDecoder( is, null );
+                    command = decoder.decode();
+                    assertThat( command.getCommandType(), is( commandType ) );
+                    assertNull( command.getData() );
+                    break;
+                case BYE_ACK:
+                    assertThat( commandType ).isSameAs( Command.BYE_ACK.getCommandType() );
+                    assertEquals( Void.class, commandType.getDataType() );
+                    encoded = commandType.encode();
+                    assertThat( new String( encoded ) )
+                            .isEqualTo( ":maven-surefire-std-out:bye-ack:" );
+                    is = new ByteArrayInputStream( encoded );
+                    decoder = new DefaultMasterProcessChannelDecoder( is, null );
+                    command = decoder.decode();
+                    assertThat( command.getCommandType(), is( commandType ) );
+                    assertNull( command.getData() );
+                    break;
+                default:
+                    fail();
+            }
+        }
+    }
+
+    public void testShouldDecodeTwoCommands() throws IOException
+    {
+        String cmd = ":maven-surefire-std-out:bye-ack\n:maven-surefire-std-out:bye-ack:";
+        InputStream is = new ByteArrayInputStream( cmd.getBytes() );
+        DefaultMasterProcessChannelDecoder decoder = new DefaultMasterProcessChannelDecoder( is, null );
+
+        Command command = decoder.decode();
+        assertThat( command.getCommandType() ).isEqualTo( BYE_ACK );
+        assertThat( command.getData() ).isNull();
+
+        command = decoder.decode();
+        assertThat( command.getCommandType() ).isEqualTo( BYE_ACK );
+        assertThat( command.getData() ).isNull();
+    }
+
+    private static byte[] addNL( byte[] encoded, char... newLines )
+    {
+        byte[] line = new byte[encoded.length + newLines.length];
+        System.arraycopy( encoded, 0, line, 0, encoded.length );
+        for ( int i = encoded.length, j = 0; i < line.length; i++, j++ )
+        {
+            line[i] = (byte) newLines[j];
+        }
+        return line;
+    }
+}
diff --git a/surefire-api/src/test/java/org/apache/maven/surefire/booter/Foo.java b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/Foo.java
similarity index 80%
rename from surefire-api/src/test/java/org/apache/maven/surefire/booter/Foo.java
rename to surefire-booter/src/test/java/org/apache/maven/surefire/booter/Foo.java
index ccb01e3..3b71b36 100644
--- a/surefire-api/src/test/java/org/apache/maven/surefire/booter/Foo.java
+++ b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/Foo.java
@@ -19,9 +19,9 @@ package org.apache.maven.surefire.booter;
  * under the License.
  */
 
-
 import java.util.Map;
 import org.apache.maven.surefire.report.ReporterConfiguration;
+import org.apache.maven.surefire.report.ReporterFactory;
 import org.apache.maven.surefire.testset.DirectoryScannerParameters;
 import org.apache.maven.surefire.testset.RunOrderParameters;
 import org.apache.maven.surefire.testset.TestArtifactInfo;
@@ -30,27 +30,28 @@ import org.apache.maven.surefire.testset.TestRequest;
 /**
  * @author Kristian Rosenvold
  */
-public class Foo
-    implements DirectoryScannerParametersAware, TestRequestAware, ProviderPropertiesAware, ReporterConfigurationAware,
-    SurefireClassLoadersAware, TestArtifactInfoAware, RunOrderParametersAware
+public class Foo extends BaseProviderFactory
 {
-    DirectoryScannerParameters directoryScannerParameters;
+    private DirectoryScannerParameters directoryScannerParameters;
 
-    Map<String, String> providerProperties;
+    private Map<String, String> providerProperties;
 
-    ReporterConfiguration reporterConfiguration;
+    private ReporterConfiguration reporterConfiguration;
 
-    ClassLoader surefireClassLoader;
+    private ClassLoader testClassLoader;
 
-    ClassLoader testClassLoader;
+    private TestRequest testRequest;
 
-    TestRequest testRequest;
+    private TestArtifactInfo testArtifactInfo;
 
-    TestArtifactInfo testArtifactInfo;
+    private RunOrderParameters runOrderParameters;
 
-    RunOrderParameters runOrderParameters;
+    private boolean called;
 
-    boolean called = false;
+    Foo()
+    {
+        super( false );
+    }
 
     @Override
     public void setDirectoryScannerParameters( DirectoryScannerParameters directoryScanner )
@@ -59,7 +60,6 @@ public class Foo
         this.called = true;
     }
 
-
     /**
      * @return true if it has been called
      */
@@ -86,7 +86,6 @@ public class Foo
     public void setClassLoaders( ClassLoader testClassLoader )
     {
         this.testClassLoader = testClassLoader;
-        this.surefireClassLoader = surefireClassLoader;
         this.called = true;
     }
 
@@ -110,4 +109,10 @@ public class Foo
         this.runOrderParameters = runOrderParameters;
         this.called = true;
     }
+
+    @Override
+    public void setReporterFactory( ReporterFactory reporterFactory )
+    {
+        called = true;
+    }
 }
diff --git a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/ForkedBooterMockTest.java b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/ForkedBooterMockTest.java
index e65beb1..24a250b 100644
--- a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/ForkedBooterMockTest.java
+++ b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/ForkedBooterMockTest.java
@@ -19,11 +19,14 @@ package org.apache.maven.surefire.booter;
  * under the License.
  */
 
+import org.apache.maven.surefire.report.StackTraceWriter;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
 import org.mockito.Mock;
 import org.powermock.api.mockito.PowerMockito;
+import org.powermock.core.classloader.annotations.PowerMockIgnore;
 import org.powermock.core.classloader.annotations.PrepareForTest;
 import org.powermock.modules.junit4.PowerMockRunner;
 
@@ -31,18 +34,24 @@ import static org.fest.assertions.Assertions.assertThat;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.same;
 import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
 import static org.powermock.api.mockito.PowerMockito.doCallRealMethod;
+import static org.powermock.api.mockito.PowerMockito.doNothing;
 import static org.powermock.api.mockito.PowerMockito.doThrow;
 import static org.powermock.api.mockito.PowerMockito.verifyNoMoreInteractions;
 import static org.powermock.api.mockito.PowerMockito.verifyPrivate;
 import static org.powermock.api.mockito.PowerMockito.when;
 import static org.powermock.reflect.Whitebox.invokeMethod;
+import static org.powermock.reflect.Whitebox.setInternalState;
+import static org.powermock.utils.JavaVersion.JAVA_RECENT;
+import static org.powermock.utils.JavaVersion.JAVA_12;
 
 /**
  * PowerMock tests for {@link ForkedBooter}.
  */
 @RunWith( PowerMockRunner.class )
-@PrepareForTest( { PpidChecker.class, ForkedBooter.class } )
+@PrepareForTest( { PpidChecker.class, ForkedBooter.class, ForkedChannelEncoder.class } )
+@PowerMockIgnore( { "org.jacoco.agent.rt.*", "com.vladium.emma.rt.*" } )
 public class ForkedBooterMockTest
 {
     @Mock
@@ -51,6 +60,21 @@ public class ForkedBooterMockTest
     @Mock
     private ForkedBooter booter;
 
+    @Mock
+    private ForkedChannelEncoder eventChannel;
+
+    @Captor
+    private ArgumentCaptor<StackTraceWriter> capturedStackTraceWriter;
+
+    @Captor
+    private ArgumentCaptor<Boolean> capturedBoolean;
+
+    @Captor
+    private ArgumentCaptor<String[]> capturedArgs;
+
+    @Captor
+    private ArgumentCaptor<ForkedBooter> capturedBooter;
+
     @Test
     public void shouldCheckNewPingMechanism() throws Exception
     {
@@ -71,8 +95,6 @@ public class ForkedBooterMockTest
     {
         PowerMockito.mockStatic( ForkedBooter.class );
 
-        ArgumentCaptor<String[]> capturedArgs = ArgumentCaptor.forClass( String[].class );
-        ArgumentCaptor<ForkedBooter> capturedBooter = ArgumentCaptor.forClass( ForkedBooter.class );
         doCallRealMethod()
                 .when( ForkedBooter.class, "run", capturedBooter.capture(), capturedArgs.capture() );
 
@@ -106,6 +128,13 @@ public class ForkedBooterMockTest
     @Test
     public void testMainWithError() throws Exception
     {
+        //does not work in PowerMock
+        //assumeFalse( JAVA_RECENT.atLeast( JAVA_12 ) );
+        if ( JAVA_RECENT.atLeast( JAVA_12 ) )
+        {
+            return;
+        }
+
         PowerMockito.mockStatic( ForkedBooter.class );
 
         doCallRealMethod()
@@ -114,6 +143,13 @@ public class ForkedBooterMockTest
         doThrow( new RuntimeException( "dummy exception" ) )
                 .when( booter, "execute" );
 
+        doNothing()
+                .when( booter, "setupBooter",
+                        any( String.class ), any( String.class ), any( String.class ), any( String.class ) );
+
+        // broken in PowerMock JDK 12
+        setInternalState( booter, "eventChannel", eventChannel );
+
         String[] args = new String[]{ "/", "dump", "surefire.properties", "surefire-effective.properties" };
         invokeMethod( ForkedBooter.class, "run", booter, args );
 
@@ -123,6 +159,18 @@ public class ForkedBooterMockTest
         verifyPrivate( booter, times( 1 ) )
                 .invoke( "execute" );
 
+        verify( eventChannel, times( 1 ) )
+                .consoleErrorLog( capturedStackTraceWriter.capture(), capturedBoolean.capture() );
+        assertThat( capturedStackTraceWriter.getValue() )
+                .isNotNull();
+        assertThat( capturedStackTraceWriter.getValue().smartTrimmedStackTrace() )
+                .isEqualTo( "test subsystem#no method RuntimeException dummy exception" );
+        assertThat( capturedStackTraceWriter.getValue().getThrowable().getTarget() )
+                .isNotNull()
+                .isInstanceOf( RuntimeException.class );
+        assertThat( capturedStackTraceWriter.getValue().getThrowable().getTarget().getMessage() )
+                .isEqualTo( "dummy exception" );
+
         verifyPrivate( booter, times( 1 ) )
                 .invoke( "cancelPingScheduler" );
 
diff --git a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/IsolatedClassLoaderTest.java b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/IsolatedClassLoaderTest.java
new file mode 100644
index 0000000..b424526
--- /dev/null
+++ b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/IsolatedClassLoaderTest.java
@@ -0,0 +1,66 @@
+package org.apache.maven.surefire.booter;
+
+/*
+ * 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.shared.utils.io.FileUtils;
+import org.apache.maven.surefire.providerapi.AbstractProvider;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
+import java.net.URL;
+
+import static java.io.File.pathSeparator;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * Tests isolated CL.
+ */
+public class IsolatedClassLoaderTest
+{
+    private IsolatedClassLoader classLoader;
+
+    @Before
+    public void prepareClassLoader() throws Exception
+    {
+        classLoader = new IsolatedClassLoader( null, false, "role" );
+
+        String[] files = FileUtils.fileRead( new File( "target/test-classpath/cp.txt" ), "UTF-8" )
+                .split( pathSeparator );
+
+        for ( String file : files )
+        {
+            URL fileUrl = new File( file ).toURL();
+            classLoader.addURL( fileUrl );
+        }
+    }
+
+    @Test
+    public void shouldLoadIsolatedClass() throws Exception
+    {
+        Class<?> isolatedClass = classLoader.loadClass( AbstractProvider.class.getName() );
+        assertThat( isolatedClass, is( notNullValue() ) );
+        assertThat( isolatedClass.getName(), is( AbstractProvider.class.getName() ) );
+        assertThat( isolatedClass, is( not( (Class) AbstractProvider.class ) ) );
+    }
+}
\ No newline at end of file
diff --git a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/JUnit4SuiteTest.java b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/JUnit4SuiteTest.java
index 75b2ef0..9ef4fce 100644
--- a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/JUnit4SuiteTest.java
+++ b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/JUnit4SuiteTest.java
@@ -35,13 +35,17 @@ public class JUnit4SuiteTest extends TestCase
     public static Test suite()
     {
         TestSuite suite = new TestSuite();
+        suite.addTest( new JUnit4TestAdapter( CommandReaderTest.class ) );
         suite.addTest( new JUnit4TestAdapter( PpidCheckerTest.class ) );
         suite.addTest( new JUnit4TestAdapter( SystemUtilsTest.class ) );
+        suite.addTest( new JUnit4TestAdapter( IsolatedClassLoaderTest.class ) );
         suite.addTest( new JUnit4TestAdapter( ForkedBooterTest.class ) );
         suite.addTest( new JUnit4TestAdapter( ForkedBooterMockTest.class ) );
         suite.addTest( new JUnit4TestAdapter( BooterDeserializerTest.class ) );
         suite.addTestSuite( ClasspathTest.class );
         suite.addTestSuite( PropertiesWrapperTest.class );
+        suite.addTestSuite( DefaultMasterProcessChannelDecoderTest.class );
+        suite.addTestSuite( SurefireReflectorTest.class );
         return suite;
     }
 }
diff --git a/surefire-api/src/test/java/org/apache/maven/surefire/booter/NewClassLoaderRunner.java b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/NewClassLoaderRunner.java
similarity index 100%
rename from surefire-api/src/test/java/org/apache/maven/surefire/booter/NewClassLoaderRunner.java
rename to surefire-booter/src/test/java/org/apache/maven/surefire/booter/NewClassLoaderRunner.java
diff --git a/surefire-api/src/test/java/org/apache/maven/surefire/booter/SurefireReflectorTest.java b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/SurefireReflectorTest.java
similarity index 78%
rename from surefire-api/src/test/java/org/apache/maven/surefire/booter/SurefireReflectorTest.java
rename to surefire-booter/src/test/java/org/apache/maven/surefire/booter/SurefireReflectorTest.java
index 0a006bf..88a4250 100644
--- a/surefire-api/src/test/java/org/apache/maven/surefire/booter/SurefireReflectorTest.java
+++ b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/SurefireReflectorTest.java
@@ -20,7 +20,6 @@ package org.apache.maven.surefire.booter;
  */
 
 import junit.framework.TestCase;
-import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
 import org.apache.maven.surefire.report.ReporterConfiguration;
 import org.apache.maven.surefire.report.ReporterFactory;
 import org.apache.maven.surefire.report.RunListener;
@@ -31,7 +30,6 @@ import org.apache.maven.surefire.testset.TestArtifactInfo;
 import org.apache.maven.surefire.testset.TestListResolver;
 import org.apache.maven.surefire.testset.TestRequest;
 import org.apache.maven.surefire.util.RunOrder;
-import org.mockito.ArgumentCaptor;
 
 import java.io.File;
 import java.lang.reflect.Method;
@@ -39,38 +37,12 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
 
-import static org.fest.assertions.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
 /**
  *
  */
 public class SurefireReflectorTest
         extends TestCase
 {
-    public void testCreateConsoleLogger()
-    {
-        ClassLoader cl = Thread.currentThread().getContextClassLoader();
-        ConsoleLogger consoleLogger = mock( ConsoleLogger.class );
-        ConsoleLogger decorator = (ConsoleLogger) SurefireReflector.createConsoleLogger( consoleLogger, cl );
-        assertThat( decorator )
-        .isNotSameAs( consoleLogger );
-
-        assertThat( decorator.isDebugEnabled() ).isFalse();
-        when( consoleLogger.isDebugEnabled() ).thenReturn( true );
-        assertThat( decorator.isDebugEnabled() ).isTrue();
-        verify( consoleLogger, times( 2 ) ).isDebugEnabled();
-
-        decorator.info( "msg" );
-        ArgumentCaptor<String> argumentMsg = ArgumentCaptor.forClass( String.class );
-        verify( consoleLogger, times( 1 ) ).info( argumentMsg.capture() );
-        assertThat( argumentMsg.getAllValues() ).hasSize( 1 );
-        assertThat( argumentMsg.getAllValues().get( 0 ) ).isEqualTo( "msg" );
-    }
-
     public void testShouldCreateFactoryWithoutException()
     {
         ReporterFactory factory = new ReporterFactory()
@@ -89,10 +61,10 @@ public class SurefireReflectorTest
         };
         ClassLoader cl = Thread.currentThread().getContextClassLoader();
         SurefireReflector reflector = new SurefireReflector( cl );
-        BaseProviderFactory baseProviderFactory =
-                (BaseProviderFactory) reflector.createBooterConfiguration( cl, factory, true );
-        assertNotNull( baseProviderFactory.getReporterFactory() );
-        assertSame( factory, baseProviderFactory.getReporterFactory() );
+        BaseProviderFactory bpf = (BaseProviderFactory) reflector.createBooterConfiguration( cl, true );
+        bpf.setReporterFactory( factory );
+        assertNotNull( bpf.getReporterFactory() );
+        assertSame( factory, bpf.getReporterFactory() );
     }
 
     public void testSetDirectoryScannerParameters()
@@ -172,6 +144,30 @@ public class SurefireReflectorTest
         assertTrue( isCalled( foo ) );
     }
 
+    public void testReporterFactoryAware()
+    {
+        SurefireReflector surefireReflector = getReflector();
+        Object foo = getFoo();
+
+        ReporterFactory reporterFactory = new ReporterFactory()
+        {
+            @Override
+            public RunListener createReporter()
+            {
+                return null;
+            }
+
+            @Override
+            public RunResult close()
+            {
+                return null;
+            }
+        };
+
+        surefireReflector.setReporterFactory( foo, reporterFactory );
+        assertTrue( isCalled( foo ) );
+    }
+
     private SurefireReflector getReflector()
     {
         return new SurefireReflector( this.getClass().getClassLoader() );
diff --git a/surefire-extensions-api/pom.xml b/surefire-extensions-api/pom.xml
index 1e88403..87fcff4 100644
--- a/surefire-extensions-api/pom.xml
+++ b/surefire-extensions-api/pom.xml
@@ -38,11 +38,6 @@
             <scope>provided</scope>
         </dependency>
         <dependency>
-            <groupId>org.codehaus.plexus</groupId>
-            <artifactId>plexus-component-annotations</artifactId>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
             <groupId>com.google.code.findbugs</groupId>
             <artifactId>jsr305</artifactId>
             <scope>provided</scope>
@@ -72,33 +67,20 @@
                 <artifactId>jacoco-maven-plugin</artifactId>
                 <executions>
                     <execution>
-                        <id>jacoco-instrumentation</id>
-                        <!--
-                        phase: the order of the plugins matters and maven-plugin-plugin must be before jacoco plugin
-                        -->
-                        <goals>
-                            <goal>instrument</goal>
-                        </goals>
-                    </execution>
-                    <execution>
-                        <id>restore-classes</id>
+                        <id>jacoco-agent</id>
                         <goals>
-                            <goal>restore-instrumented-classes</goal>
+                            <goal>prepare-agent</goal>
                         </goals>
                     </execution>
                 </executions>
+                <configuration>
+                    <propertyName>jacoco.agent</propertyName>
+                </configuration>
             </plugin>
             <plugin>
                 <artifactId>maven-surefire-plugin</artifactId>
-                <dependencies>
-                    <dependency>
-                        <groupId>org.apache.maven.surefire</groupId>
-                        <artifactId>surefire-shadefire</artifactId>
-                        <version>3.0.0-M4</version> <!-- ${shadedVersion}, but resolved due to https://issues.apache.org/jira/browse/MRELEASE-799 -->
-                    </dependency>
-                </dependencies>
                 <configuration>
-                    <argLine>${jvm.args.tests}</argLine>
+                    <argLine>${jvm.args.tests} ${jacoco.agent}</argLine>
                     <includes>
                         <include>**/JUnit4SuiteTest.java</include>
                     </includes>
@@ -106,8 +88,14 @@
                         <jacoco-agent.destfile>${project.build.directory}/jacoco.exec</jacoco-agent.destfile>
                     </systemPropertyVariables>
                 </configuration>
+                <dependencies>
+                    <dependency>
+                        <groupId>org.apache.maven.surefire</groupId>
+                        <artifactId>surefire-shadefire</artifactId>
+                        <version>3.0.0-M4</version> <!-- ${shadedVersion}, but resolved due to https://issues.apache.org/jira/browse/MRELEASE-799 -->
+                    </dependency>
+                </dependencies>
             </plugin>
         </plugins>
     </build>
-
 </project>
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/MainCliOptionsAware.java b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/CommandReader.java
similarity index 64%
copy from surefire-api/src/main/java/org/apache/maven/surefire/booter/MainCliOptionsAware.java
copy to surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/CommandReader.java
index eddebed..8dd9d96 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/MainCliOptionsAware.java
+++ b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/CommandReader.java
@@ -1,4 +1,4 @@
-package org.apache.maven.surefire.booter;
+package org.apache.maven.surefire.extensions;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -19,17 +19,26 @@ package org.apache.maven.surefire.booter;
  * under the License.
  */
 
-import org.apache.maven.surefire.cli.CommandLineOption;
+import org.apache.maven.surefire.booter.Command;
 
-import java.util.List;
+import java.io.IOException;
 
 /**
- * CLI options in plugin (main) JVM process.
+ * Stream reader returns bytes which ar finally sent to the forked jvm.
  *
  * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
- * @since 2.19
+ * @since 3.0.0-M4
  */
-interface MainCliOptionsAware
+public interface CommandReader
 {
-    void setMainCliOptions( List<CommandLineOption> mainCliOptions );
+
+    /**
+     * Waits for the next command and returns it.
+     *
+     * @return the command, or null if closed
+     */
+    Command readNextCommand() throws IOException;
+    void close();
+    boolean isClosed();
+    void tryFlush() throws IOException;
 }
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/FailFastAware.java b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/EventHandler.java
similarity index 68%
rename from surefire-api/src/main/java/org/apache/maven/surefire/booter/FailFastAware.java
rename to surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/EventHandler.java
index 994b60d..bcb6c81 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/FailFastAware.java
+++ b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/EventHandler.java
@@ -1,4 +1,4 @@
-package org.apache.maven.surefire.booter;
+package org.apache.maven.surefire.extensions;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -19,13 +19,14 @@ package org.apache.maven.surefire.booter;
  * under the License.
  */
 
+import javax.annotation.Nonnull;
+
 /**
- * See the plugin configuration parameter {@code skipAfterFailureCount}.
  *
- * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
- * @since 2.19
  */
-interface FailFastAware
+public interface EventHandler
 {
-    void setSkipAfterFailureCount( int skipAfterFailureCount );
+    // todo here should be Event object as POJO which separates physical stream representation from the independent
+    // todo Event parser in ForkClient. So the parser should be part of ForkChannel implementation.
+    void handleEvent( @Nonnull String event );
 }
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/DirectoryScannerParametersAware.java b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/ExecutableCommandline.java
similarity index 53%
rename from surefire-api/src/main/java/org/apache/maven/surefire/booter/DirectoryScannerParametersAware.java
rename to surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/ExecutableCommandline.java
index cefeb33..b373002 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/DirectoryScannerParametersAware.java
+++ b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/ExecutableCommandline.java
@@ -1,4 +1,4 @@
-package org.apache.maven.surefire.booter;
+package org.apache.maven.surefire.extensions;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -19,12 +19,20 @@ package org.apache.maven.surefire.booter;
  * under the License.
  */
 
-import org.apache.maven.surefire.testset.DirectoryScannerParameters;
+import javax.annotation.Nonnull;
+import java.util.concurrent.Callable;
 
 /**
- * @author Kristian Rosenvold
+ * todo add javadoc
  */
-interface DirectoryScannerParametersAware
+public interface ExecutableCommandline
 {
-    void setDirectoryScannerParameters( DirectoryScannerParameters directoryScanner );
+    @Nonnull
+    <T> Callable<Integer> executeCommandLineAsCallable( @Nonnull T cli,
+                                                        @Nonnull CommandReader commands,
+                                                        @Nonnull EventHandler events,
+                                                        StdOutStreamLine stdOut,
+                                                        StdErrStreamLine stdErr,
+                                                        @Nonnull Runnable runAfterProcessTermination )
+            throws Exception;
 }
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/MainCliOptionsAware.java b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/ForkChannel.java
similarity index 52%
copy from surefire-api/src/main/java/org/apache/maven/surefire/booter/MainCliOptionsAware.java
copy to surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/ForkChannel.java
index eddebed..62ef9bf 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/MainCliOptionsAware.java
+++ b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/ForkChannel.java
@@ -1,4 +1,4 @@
-package org.apache.maven.surefire.booter;
+package org.apache.maven.surefire.extensions;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -19,17 +19,24 @@ package org.apache.maven.surefire.booter;
  * under the License.
  */
 
-import org.apache.maven.surefire.cli.CommandLineOption;
-
-import java.util.List;
+import javax.annotation.Nonnull;
+import java.io.Closeable;
+import java.io.IOException;
 
 /**
- * CLI options in plugin (main) JVM process.
- *
- * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
- * @since 2.19
+ * The constructor prepares I/O or throws {@link IOException}. Open channel can be closed and closes all streams.
+ * <br>
+ * The forked JVM uses the {@link #createExecutableCommandline() connection string}.
+ * The executable CLI {@link #createExecutableCommandline()} is using the streams. This method and constructor should
+ * not be blocked while establishing the connection.
  */
-interface MainCliOptionsAware
+public interface ForkChannel extends Closeable
 {
-    void setMainCliOptions( List<CommandLineOption> mainCliOptions );
+    String getForkNodeConnectionString();
+
+    @Nonnull
+    ExecutableCommandline createExecutableCommandline() throws IOException;
+
+    @Override
+    void close() throws IOException;
 }
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/MainCliOptionsAware.java b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/ForkNodeFactory.java
similarity index 69%
copy from surefire-api/src/main/java/org/apache/maven/surefire/booter/MainCliOptionsAware.java
copy to surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/ForkNodeFactory.java
index eddebed..c054776 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/MainCliOptionsAware.java
+++ b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/ForkNodeFactory.java
@@ -1,4 +1,4 @@
-package org.apache.maven.surefire.booter;
+package org.apache.maven.surefire.extensions;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -19,17 +19,20 @@ package org.apache.maven.surefire.booter;
  * under the License.
  */
 
-import org.apache.maven.surefire.cli.CommandLineOption;
-
-import java.util.List;
+import javax.annotation.Nonnull;
+import java.io.IOException;
 
 /**
- * CLI options in plugin (main) JVM process.
- *
  * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
- * @since 2.19
+ * @since 3.0.0-M4
  */
-interface MainCliOptionsAware
+public interface ForkNodeFactory
 {
-    void setMainCliOptions( List<CommandLineOption> mainCliOptions );
+    /**
+     * Opens and closes the channel.
+     *
+     * @return specific implementation of the communication channel
+     * @throws IOException if cannot open the channel
+     */
+    @Nonnull ForkChannel createForkChannel() throws IOException;
 }
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/SurefireClassLoadersAware.java b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/StdErrStreamLine.java
similarity index 82%
rename from surefire-api/src/main/java/org/apache/maven/surefire/booter/SurefireClassLoadersAware.java
rename to surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/StdErrStreamLine.java
index c2f5d99..be444ae 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/SurefireClassLoadersAware.java
+++ b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/StdErrStreamLine.java
@@ -1,4 +1,4 @@
-package org.apache.maven.surefire.booter;
+package org.apache.maven.surefire.extensions;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -20,9 +20,9 @@ package org.apache.maven.surefire.booter;
  */
 
 /**
- * @author Kristian Rosenvold
+ * The line handler of forked process standard-error.
  */
-interface SurefireClassLoadersAware
+public interface StdErrStreamLine
 {
-    void setClassLoaders( ClassLoader testClassLoader );
+    void handleLine( String line );
 }
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/ProviderPropertiesAware.java b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/StdOutStreamLine.java
similarity index 80%
rename from surefire-api/src/main/java/org/apache/maven/surefire/booter/ProviderPropertiesAware.java
rename to surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/StdOutStreamLine.java
index caedb98..f615764 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/ProviderPropertiesAware.java
+++ b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/StdOutStreamLine.java
@@ -1,4 +1,4 @@
-package org.apache.maven.surefire.booter;
+package org.apache.maven.surefire.extensions;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -19,12 +19,10 @@ package org.apache.maven.surefire.booter;
  * under the License.
  */
 
-import java.util.Map;
-
 /**
- * @author Kristian Rosenvold
+ * The line handler of forked process standard-output.
  */
-interface ProviderPropertiesAware
+public interface StdOutStreamLine
 {
-    void setProviderProperties( Map<String, String> providerProperties );
+    void handleLine( String line );
 }
diff --git a/surefire-extensions-spi/pom.xml b/surefire-extensions-spi/pom.xml
new file mode 100644
index 0000000..95cf3d9
--- /dev/null
+++ b/surefire-extensions-spi/pom.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.maven.surefire</groupId>
+        <artifactId>surefire</artifactId>
+        <version>3.0.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>surefire-extensions-spi</artifactId>
+    <name>Surefire Extensions SPI</name>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.maven.surefire</groupId>
+            <artifactId>surefire-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/MainCliOptionsAware.java b/surefire-extensions-spi/src/main/java/org/apache/maven/surefire/spi/MasterProcessChannelDecoderFactory.java
similarity index 57%
rename from surefire-api/src/main/java/org/apache/maven/surefire/booter/MainCliOptionsAware.java
rename to surefire-extensions-spi/src/main/java/org/apache/maven/surefire/spi/MasterProcessChannelDecoderFactory.java
index eddebed..5b08d4d 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/MainCliOptionsAware.java
+++ b/surefire-extensions-spi/src/main/java/org/apache/maven/surefire/spi/MasterProcessChannelDecoderFactory.java
@@ -1,4 +1,4 @@
-package org.apache.maven.surefire.booter;
+package org.apache.maven.surefire.spi;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -19,17 +19,22 @@ package org.apache.maven.surefire.booter;
  * under the License.
  */
 
-import org.apache.maven.surefire.cli.CommandLineOption;
+import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
+import org.apache.maven.surefire.providerapi.MasterProcessChannelDecoder;
 
-import java.util.List;
+import java.io.IOException;
 
 /**
- * CLI options in plugin (main) JVM process.
- *
- * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
- * @since 2.19
+ * The SPI interface, a factory of decoders.
  */
-interface MainCliOptionsAware
+public interface MasterProcessChannelDecoderFactory
 {
-    void setMainCliOptions( List<CommandLineOption> mainCliOptions );
+    /**
+     * Decoder factory method.
+     *
+     * @param channelConfig "pipe:std:in" or "tcp://localhost:65035"
+     * @param logger        error logger
+     * @return a new instance of decoder
+     */
+    MasterProcessChannelDecoder createDecoder( String channelConfig, ConsoleLogger logger ) throws IOException;
 }
diff --git a/surefire-providers/surefire-junit4/src/main/java/org/apache/maven/surefire/junit4/JUnit4Provider.java b/surefire-providers/surefire-junit4/src/main/java/org/apache/maven/surefire/junit4/JUnit4Provider.java
index b964933..9e5efad 100644
--- a/surefire-providers/surefire-junit4/src/main/java/org/apache/maven/surefire/junit4/JUnit4Provider.java
+++ b/surefire-providers/surefire-junit4/src/main/java/org/apache/maven/surefire/junit4/JUnit4Provider.java
@@ -20,8 +20,8 @@ package org.apache.maven.surefire.junit4;
  */
 
 import org.apache.maven.surefire.booter.Command;
-import org.apache.maven.surefire.booter.CommandListener;
-import org.apache.maven.surefire.booter.CommandReader;
+import org.apache.maven.surefire.providerapi.CommandChainReader;
+import org.apache.maven.surefire.providerapi.CommandListener;
 import org.apache.maven.surefire.common.junit4.JUnit4RunListener;
 import org.apache.maven.surefire.common.junit4.JUnit4TestChecker;
 import org.apache.maven.surefire.common.junit4.JUnitTestFailureListener;
@@ -52,7 +52,6 @@ import java.util.Set;
 
 import static java.lang.reflect.Modifier.isAbstract;
 import static java.lang.reflect.Modifier.isInterface;
-import static org.apache.maven.surefire.booter.CommandReader.getReader;
 import static org.apache.maven.surefire.common.junit4.JUnit4ProviderUtil.createMatchAnyDescriptionFilter;
 import static org.apache.maven.surefire.common.junit4.JUnit4ProviderUtil.generateFailingTestDescriptions;
 import static org.apache.maven.surefire.common.junit4.JUnit4ProviderUtil.isFailureInsideJUnitItself;
@@ -92,14 +91,14 @@ public class JUnit4Provider
 
     private final int rerunFailingTestsCount;
 
-    private final CommandReader commandsReader;
+    private final CommandChainReader commandsReader;
 
     private TestsToRun testsToRun;
 
     public JUnit4Provider( ProviderParameters bootParams )
     {
         // don't start a thread in CommandReader while we are in in-plugin process
-        commandsReader = bootParams.isInsideFork() ? getReader().setShutdown( bootParams.getShutdown() ) : null;
+        commandsReader = bootParams.isInsideFork() ? bootParams.getCommandReader() : null;
         providerParameters = bootParams;
         testClassLoader = bootParams.getTestClassLoader();
         scanResult = bootParams.getScanResult();
diff --git a/surefire-providers/surefire-junit4/src/test/java/org/apache/maven/surefire/junit4/JUnit4ProviderTest.java b/surefire-providers/surefire-junit4/src/test/java/org/apache/maven/surefire/junit4/JUnit4ProviderTest.java
index b949baa..c03ea7e 100644
--- a/surefire-providers/surefire-junit4/src/test/java/org/apache/maven/surefire/junit4/JUnit4ProviderTest.java
+++ b/surefire-providers/surefire-junit4/src/test/java/org/apache/maven/surefire/junit4/JUnit4ProviderTest.java
@@ -50,7 +50,7 @@ public class JUnit4ProviderTest
 
     private JUnit4Provider getJUnit4Provider()
     {
-        BaseProviderFactory providerParameters = new BaseProviderFactory( null, true );
+        BaseProviderFactory providerParameters = new BaseProviderFactory( true );
         providerParameters.setProviderProperties( new HashMap<String, String>() );
         providerParameters.setClassLoaders( getClass().getClassLoader() );
         providerParameters.setTestRequest( new TestRequest( null, null, null ) );
diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreProvider.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreProvider.java
index 7c74e8b..daabb60 100644
--- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreProvider.java
+++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreProvider.java
@@ -20,8 +20,8 @@ package org.apache.maven.surefire.junitcore;
  */
 
 import org.apache.maven.surefire.booter.Command;
-import org.apache.maven.surefire.booter.CommandListener;
-import org.apache.maven.surefire.booter.CommandReader;
+import org.apache.maven.surefire.providerapi.CommandChainReader;
+import org.apache.maven.surefire.providerapi.CommandListener;
 import org.apache.maven.surefire.common.junit4.JUnit4RunListener;
 import org.apache.maven.surefire.common.junit4.JUnitTestFailureListener;
 import org.apache.maven.surefire.common.junit4.Notifier;
@@ -46,7 +46,6 @@ import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
-import static org.apache.maven.surefire.booter.CommandReader.getReader;
 import static org.apache.maven.surefire.common.junit4.JUnit4ProviderUtil.generateFailingTestDescriptions;
 import static org.apache.maven.surefire.common.junit4.JUnit4RunListenerFactory.createCustomListeners;
 import static org.apache.maven.surefire.common.junit4.Notifier.pureNotifier;
@@ -82,14 +81,14 @@ public class JUnitCoreProvider
 
     private final TestListResolver testResolver;
 
-    private final CommandReader commandsReader;
+    private final CommandChainReader commandsReader;
 
     private TestsToRun testsToRun;
 
     public JUnitCoreProvider( ProviderParameters bootParams )
     {
         // don't start a thread in CommandReader while we are in in-plugin process
-        commandsReader = bootParams.isInsideFork() ? getReader().setShutdown( bootParams.getShutdown() ) : null;
+        commandsReader = bootParams.isInsideFork() ? bootParams.getCommandReader() : null;
         providerParameters = bootParams;
         testClassLoader = bootParams.getTestClassLoader();
         scanResult = bootParams.getScanResult();
diff --git a/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/Surefire746Test.java b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/Surefire746Test.java
index 8859c19..66f0b85 100644
--- a/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/Surefire746Test.java
+++ b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/Surefire746Test.java
@@ -110,7 +110,8 @@ public class Surefire746Test
     public void surefireIsConfused_ByMultipleIgnore_OnClassLevel() throws Exception
     {
         ReporterFactory reporterFactory = JUnitCoreTester.defaultNoXml();
-        BaseProviderFactory providerParameters = new BaseProviderFactory( reporterFactory, true );
+        BaseProviderFactory providerParameters = new BaseProviderFactory( true );
+        providerParameters.setReporterFactory( reporterFactory );
 
         providerParameters.setReporterConfiguration( new ReporterConfiguration( new File( "" ), false ) );
         Map<String, String> junitProps = new HashMap<>();
diff --git a/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGProvider.java b/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGProvider.java
index 14e103a..a8a5fb7 100644
--- a/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGProvider.java
+++ b/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGProvider.java
@@ -20,8 +20,8 @@ package org.apache.maven.surefire.testng;
  */
 
 import org.apache.maven.surefire.booter.Command;
-import org.apache.maven.surefire.booter.CommandListener;
-import org.apache.maven.surefire.booter.CommandReader;
+import org.apache.maven.surefire.providerapi.CommandChainReader;
+import org.apache.maven.surefire.providerapi.CommandListener;
 import org.apache.maven.surefire.cli.CommandLineOption;
 import org.apache.maven.surefire.providerapi.AbstractProvider;
 import org.apache.maven.surefire.providerapi.ProviderParameters;
@@ -43,7 +43,6 @@ import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 
-import static org.apache.maven.surefire.booter.CommandReader.getReader;
 import static org.apache.maven.surefire.report.ConsoleOutputCapture.startCapture;
 import static org.apache.maven.surefire.testset.TestListResolver.getEmptyTestListResolver;
 import static org.apache.maven.surefire.testset.TestListResolver.optionallyWildcardFilter;
@@ -71,14 +70,14 @@ public class TestNGProvider
 
     private final List<CommandLineOption> mainCliOptions;
 
-    private final CommandReader commandsReader;
+    private final CommandChainReader commandsReader;
 
     private TestsToRun testsToRun;
 
     public TestNGProvider( ProviderParameters bootParams )
     {
         // don't start a thread in CommandReader while we are in in-plugin process
-        commandsReader = bootParams.isInsideFork() ? getReader().setShutdown( bootParams.getShutdown() ) : null;
+        commandsReader = bootParams.isInsideFork() ? bootParams.getCommandReader() : null;
         providerParameters = bootParams;
         testClassLoader = bootParams.getTestClassLoader();
         runOrderCalculator = bootParams.getRunOrderCalculator();


[maven-surefire] 03/12: fixed few tests after bad merge

Posted by ti...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit c070d9ecf9f832ce29f7db839eabf17240490944
Author: tibordigana <ti...@apache.org>
AuthorDate: Sun Jan 12 21:31:18 2020 +0100

    fixed few tests after bad merge
---
 pom.xml                                                    |  1 -
 .../maven/surefire/util/internal/DaemonThreadFactory.java  |  9 +++++++++
 surefire-booter/pom.xml                                    | 14 --------------
 3 files changed, 9 insertions(+), 15 deletions(-)

diff --git a/pom.xml b/pom.xml
index 49f0b14..e9a6763 100644
--- a/pom.xml
+++ b/pom.xml
@@ -498,7 +498,6 @@
                 <compilerArgs>
                   <arg>-Xdoclint:all</arg>
                 </compilerArgs>
-                <verbose>true</verbose>
               </configuration>
             </execution>
           </executions>
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/DaemonThreadFactory.java b/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/DaemonThreadFactory.java
index 06ddc53..32b597a 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/DaemonThreadFactory.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/DaemonThreadFactory.java
@@ -21,6 +21,7 @@ package org.apache.maven.surefire.util.internal;
 
 import java.util.concurrent.Executors;
 import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * Creates new daemon Thread.
@@ -30,14 +31,22 @@ public final class DaemonThreadFactory
 {
     private static final ThreadFactory DEFAULT_THREAD_FACTORY = Executors.defaultThreadFactory();
 
+    private static final AtomicInteger POOL_NUMBER = new AtomicInteger( 1 );
+
+    private final AtomicInteger threadNumber = new AtomicInteger( 1 );
+
+    private final String namePrefix;
+
     private DaemonThreadFactory()
     {
+        namePrefix = "pool-" + POOL_NUMBER.getAndIncrement() + "-thread-";
     }
 
     @Override
     public Thread newThread( Runnable r )
     {
         Thread t = DEFAULT_THREAD_FACTORY.newThread( r );
+        t.setName( namePrefix + threadNumber.getAndIncrement() );
         t.setDaemon( true );
         return t;
     }
diff --git a/surefire-booter/pom.xml b/surefire-booter/pom.xml
index 6ba9af1..a0dbd9a 100644
--- a/surefire-booter/pom.xml
+++ b/surefire-booter/pom.xml
@@ -36,12 +36,6 @@
       <groupId>org.apache.maven.surefire</groupId>
       <artifactId>surefire-api</artifactId>
       <version>${project.version}</version>
-      <exclusions>
-        <exclusion>
-          <groupId>org.apache.maven.shared</groupId>
-          <artifactId>maven-shared-utils</artifactId>
-        </exclusion>
-      </exclusions>
     </dependency>
     <dependency>
       <groupId>org.apache.maven.surefire</groupId>
@@ -54,14 +48,6 @@
       <scope>test</scope>
     </dependency>
     <dependency>
-      <groupId>org.apache.commons</groupId>
-      <artifactId>commons-lang3</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>commons-io</groupId>
-      <artifactId>commons-io</artifactId>
-    </dependency>
-    <dependency>
       <groupId>com.google.code.findbugs</groupId>
       <artifactId>jsr305</artifactId>
       <scope>provided</scope>


[maven-surefire] 06/12: investigate dumps on Ubuntu

Posted by ti...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 4be01d7ddd59e9a292c840eb1cf65e2915800e29
Author: tibordigana <ti...@apache.org>
AuthorDate: Mon Jan 13 01:23:23 2020 +0100

    investigate dumps on Ubuntu
---
 .github/workflows/maven.yml | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index 0d56bb9..f190c35 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -40,3 +40,9 @@ jobs:
 
       - name: Build with Maven
         run: mvn install -e -B -V -nsu --no-transfer-progress -P run-its
+
+      - name: cat dump
+        run: cat surefire-logger-api/target/surefire-reports/*.dump
+
+      - name: cat dumpstream
+        run: cat surefire-logger-api/target/surefire-reports/*.dumpstream


[maven-surefire] 07/12: -fn

Posted by ti...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 83b7f525773c1bc6f2fe97eea9255858d4c482bc
Author: tibordigana <ti...@apache.org>
AuthorDate: Mon Jan 13 01:26:15 2020 +0100

    -fn
---
 .github/workflows/maven.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index f190c35..26b7bf0 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -39,7 +39,7 @@ jobs:
           java-version: 1.8
 
       - name: Build with Maven
-        run: mvn install -e -B -V -nsu --no-transfer-progress -P run-its
+        run: mvn install -e -B -V -nsu --no-transfer-progress -P run-its -fn
 
       - name: cat dump
         run: cat surefire-logger-api/target/surefire-reports/*.dump


[maven-surefire] 05/12: removed '-DskipTests', '-DskipITs'

Posted by ti...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit b413542315cbbd610bceab5f966a931232431ff3
Author: tibordigana <ti...@apache.org>
AuthorDate: Sun Jan 12 22:10:55 2020 +0100

    removed '-DskipTests', '-DskipITs'
---
 Jenkinsfile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Jenkinsfile b/Jenkinsfile
index ac8c07f..a37148c 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -35,7 +35,7 @@ final def mavens = env.BRANCH_NAME == 'master' ? ['3.6.x', '3.2.x'] : ['3.6.x']
 // all non-EOL versions and the first EA
 final def jdks = [14, 13, 11, 8, 7]
 
-final def options = ['-e', '-V', '-B', '-nsu', '-DskipTests', '-DskipITs']
+final def options = ['-e', '-V', '-B', '-nsu']
 final def goals = ['clean', 'install']
 final def goalsDepl = ['clean', 'deploy', 'jacoco:report']
 final Map stages = [:]


[maven-surefire] 11/12: investigate only Ubuntu

Posted by ti...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit f0fd66c96e9af356325b39a38042ee71adb4e20f
Author: tibordigana <ti...@apache.org>
AuthorDate: Mon Jan 13 02:22:37 2020 +0100

    investigate only Ubuntu
---
 .github/workflows/maven.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index b1f0eb2..d9e509c 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -24,7 +24,7 @@ jobs:
 
     strategy:
       matrix:
-        os: [ubuntu-latest, windows-latest, macOS-latest]
+        os: [ubuntu-latest]
       fail-fast: false
 
     runs-on: ${{ matrix.os }}