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/01 01:22:14 UTC

[maven-surefire] branch maven2surefire-jvm-communication updated (f7853e4 -> 60c79d6)

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 f7853e4  improved the loop
    omit 95f93bf  set server socket options
    omit c121165  simplified writes
    omit cc59f07  buffer flip
    omit 018ce06  checkstyle
    omit f92816d  TCP server
    omit 795bda5  small two changes
    omit 33bbb50  improved DaemonThreadFactory
    omit a69ad60  resolved the previous problems
    omit 60c0201  skipped tests in CI
    omit fae7bd7  fixed compilation errors (two tests hang, and see: todo stdOut)
    omit 9a7d2a2  proposed abstraction for Enrico's implementation
    omit 3128495  [SUREFIRE-1658] TCP/IP Channel for forked Surefire JVM. Extensions API and SPI. Polymorphism for remote and local process communication.
     add 4a5aa08  prerequisites/maven 3.0 only in plugins
     add 8054026  TestNG 7.0.0 instead of 7.0.0-beta-7 in the integration tests
     add 9dc7f29  [SUREFIRE-1690] Typo fixed: classpathDependencyExclude (#246)
     add f39ab02  typo-fix
     add bd84e3c  Updated JUnit5 Version to 5.5.2 in the integration tests
     add d0230dc  [SUREFIRE-1584] Add support for rerunFailingTests to JUnit5 Provider
     add 17bbc18  temporarily disabled JUnit5 1.6.0-SNAPSHOT due to Sonatype issue OSSRH-51220
     add 49887e0  temporarily disabled JUnit5 1.6.0-SNAPSHOT due to Sonatype issue OSSRH-51220
     add 7aacfbf  JDK 14 EA
     add 9e7af4e  enabled JUnit5 1.6.0-SNAPSHOT due to Sonatype issue OSSRH-51220
     add 29c01bb  github workflows with maven.yml
     add 08c7954  excluded **/.github/** in maven-rat-plugin
     add bdf2e83  static code analysis
     add 63f37ce  Revert "static code analysis" (commit was a mistake in master, target branch was SUREFIRE-1689)
     add 5ee0b53  Update JaCoCo to 0.8.5 (supports J13 and J14)
     add c512670  temporarily disabled JUnit5 5.6.0-SNAPSHOT and 1.6.0-SNAPSHOT due to Sonatype issue OSSRH-51220
     add e90999f  [SUREFIRE-1689] The fast PpidChecker is switched to the slow 30 seconds PING after the subprocess (/bin/ps -o etime= -p ...) failed with exit 1
     add 3d101c2  [SUREFIRE-1668] The stackTrace should use CDATA in XML report.
     add b97df5a  Update Powermock to version 2.0.4
     add 8a886f3  [SUREFIRE-1702] [JDK 11 Alpine Linux] JAR content is not flushed completely down to drive "Error: Invalid or corrupt jarfile target/surefire/surefirebooter13749914711390838584.jar"
     add 55468d1  JUnit5 5.6.0-M1 in test plan
     add 701e918  [INFRA-19342] excluded jenkins-win-he-de-4 and H42
     add cf86805  [SUREFIRE-1703] [JDK 11 Alpine Linux] Surefire handled random order of pid and /procps does not filter pid on busybox distributions
     add 0595e4e  [SUREFIRE-1704] [JDK 11 Alpine Linux] long etime within hours has format 2h01 on busybox
     add c391947  [SUREFIRE-1701] Use literal names instead of display names for classes/methods for test reruns
     add 254cce6  '--no-transfer-progress' for 3.6.1+ builds
     add 80659dc  [SUREFIRE-1705] new config parameter Exclude Environment Variables
     add 8801286  [jenkinsfile] enabled H42 and jenkins-win-he-de-4
     add 541e6b8  [SUREFIRE-1706] Use the checkstyle in tests and se includeTestSourceDirectory=true
     add 8c95e45  [SUREFIRE-1707] Forked JVM is killed when GC paused the tests for over 30 seconds
     add 2bea7c8  [jenkinsfile] -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn
     add b2cce57  [SUREFIRE-1711] Support @ParameterizedTest for JUnit 5 test reruns
     add fef545b  fixed checkstyle typo
     add 615b647  [github actions] minimized the wagon logs
     add 8a08472  [github actions] --no-transfer-progress
     add cca79ff  [travisci] mvn verify runs the checkstyle
     add 14ef106  [travisci] mvn install
     add 8d62f0c  [travisci] [github actions] -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn
     add 6bae9ba  [github actions] removed -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn
     add 46cd88c  [SUREFIRE-1712] Running tests with JDK13 fails with Unsupported class file major version 57
     add 7e11fe3  [SUREFIRE-1714] Created module "surefire-shared-utils" as a required dependency in "surefire-extensions-api" and "maven-surefire-common"
     add 9d23be0  [SUREFIRE-1714] Created module "surefire-shared-utils" as a required dependency in "surefire-extensions-api" and "maven-surefire-common"
     add 43cb7ce  dependency versions should be in parent POM
     add 3a6b00a  integration tests with Java 9+ should use <maven.compiler.release>${java.specification.version}</maven.compiler.release>
     add 6e5ecba  H31 gives OOME (let's see if others are stable again...)
     add 8a769ca  scheduleAtFixedRate() runs too frequent runs after GC paused the scheduler
     add 891a7b9  dump command output when fails
     add 6c6b834  removed cleanWs() after the checkout - ensured at the end
     add c49623a  [github actions] ASF license, OS matrix, run on push and PR
     add 0c0d902  [SUREFIRE-1716] JUnit5 Parameterized tests and re-run should see unique test runs with different parameters
     add 08ff28f  [SUREFIRE-1717] Enable Process Checkers
     add fd451b5  few more unit tests
     add 25ee208  roadmap after 3.0.0-M4
     add b0325d4  [maven-release-plugin] prepare release surefire-3.0.0-M4_vote-1
     add ed630bc  [maven-release-plugin] prepare for next development iteration
     add f673d24  [SUREFIRE-1721] fixed typo in JavaDoc for Failsafe: mvn test -Dsurefire.enableProcessChecker=all
     add cdbd5ce  fixed unstable build #102 with CheckTestNgExecuteErrorIT.java
     add ce0e203  added link of GitHub CI to README
     add 47bfda1  README color
     add 970b708  [SUREFIRE-1378] Nice to have systemPropertiesFile configurable by user property
     add 695a44f  added Git .editorconfig
     add 5d8c408  if we use -DskipTests we really want to skip tests so IT tests should be not executed
     add 8628fe3  use last surefire version available
     add ae3e0ac  [SUREFIRE-1688] Add failing test case for failure in BeforeAll method
     add 706346f  [SUREFIRE-1728] maven.test.failure.ignore: differentiate between test failure and timeout
     add e20ce36  Run Order / JUnit5 supported in the Feature Matrix + tests
     add 479d9fe  reservedPort1 and reservedPort2 handled the same TCP port with the old version build-helper-maven-plugin:1.2
     add db16777  plexus-java as a property to ease testing
     add 83b1b98  fixed the grammar in new configuration parameter 'enableProcessChecker' in 3.0.0-M4
     add d013902  a debug log printed in a condition only when necessary
     add 6d8b670  [SUREFIRE-1740] Prerequisite implementation for SUREFIRE-1658
     add 25d204c  excluded H26
     add 993214d  GitHub CI: removed on: pull_request
     add 2e65fb2  [SUREFIRE-1719] Race condition results in "VM crash or System.exit called?" failure
     add 5148b02  [SUREFIRE-1516] Poor performance in reuseForks=false
     new 60c79d6  [SUREFIRE-1658] TCP/IP Channel for forked Surefire JVM. Extensions API and SPI. Polymorphism for remote and local process communication.

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   (f7853e4)
            \
             N -- N -- N   refs/heads/maven2surefire-jvm-communication (60c79d6)

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 1 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:
 .editorconfig                                      |   8 +
 .travis.yml => .github/workflows/maven.yml         |  35 +-
 .travis.yml                                        |   2 +-
 Jenkinsfile                                        |  14 +-
 README.md                                          |  12 +-
 maven-failsafe-plugin/pom.xml                      |  52 +--
 .../maven/plugin/failsafe/IntegrationTestMojo.java |  92 +++-
 .../apache/maven/plugin/failsafe/VerifyMojo.java   |   2 +-
 .../failsafe/util/FailsafeSummaryXmlUtils.java     |   6 +-
 .../plugin/failsafe/IntegrationTestMojoTest.java   | 107 ++++-
 .../failsafe/MarshallerUnmarshallerTest.java       |   3 +
 .../maven/plugin/failsafe/RunResultTest.java       |  12 +-
 maven-surefire-common/pom.xml                      |  37 +-
 .../plugin/surefire/AbstractSurefireMojo.java      |  83 ++--
 .../surefire/StartupReportConfiguration.java       |   2 +-
 .../maven/plugin/surefire/SurefireHelper.java      |   4 +-
 .../maven/plugin/surefire/TestClassPath.java       |   2 +-
 .../AbstractClasspathForkConfiguration.java        |   3 +-
 .../surefire/booterclient/BooterSerializer.java    |  34 +-
 .../booterclient/ClasspathForkConfiguration.java   |   8 +-
 .../booterclient/DefaultForkConfiguration.java     |  13 +-
 .../surefire/booterclient/ForkConfiguration.java   |   1 +
 .../plugin/surefire/booterclient/ForkStarter.java  | 109 +++--
 .../booterclient/JarManifestForkConfiguration.java |  32 +-
 .../ModularClasspathForkConfiguration.java         |  41 +-
 .../lazytestprovider/NotifiableTestStream.java     |  10 +-
 ...eceiver.java => OutputStreamFlushReceiver.java} |  27 +-
 .../OutputStreamFlushableCommandline.java          |  51 ++-
 .../lazytestprovider/TestLessInputStream.java      |  74 +---
 .../surefire/booterclient/output/ForkClient.java   |   2 +-
 .../output/NativeStdErrStreamConsumer.java         |   2 +-
 .../output/ThreadedStreamConsumer.java             |  11 +-
 .../extensions/NetworkingProcessExecutor.java      |   4 +-
 .../surefire/extensions/PipeProcessExecutor.java   |   6 +-
 .../plugin/surefire/extensions/StdErrAdapter.java  |   2 +-
 .../plugin/surefire/extensions/StdOutAdapter.java  |   2 +-
 .../plugin/surefire/report/ConsoleReporter.java    |   4 +-
 .../surefire/report/DefaultReporterFactory.java    |   4 +-
 .../plugin/surefire/report/FileReporterUtils.java  |   2 +-
 .../surefire/report/StatelessXmlReporter.java      |  64 ++-
 .../maven/plugin/surefire/report/TestSetStats.java |   4 +-
 .../Utf8RecodingDeferredFileOutputStream.java      |   2 +-
 .../maven/plugin/surefire/util/ScannerUtil.java    |   2 +-
 .../plugin/surefire/util/SpecificFileFilter.java   |   2 +-
 .../AbstractSurefireMojoJava7PlusTest.java         |  45 +-
 .../plugin/surefire/AbstractSurefireMojoTest.java  | 132 +++++-
 .../maven/plugin/surefire/CommonReflectorTest.java |   5 +-
 .../org/apache/maven/plugin/surefire/DataZT1A.java |   7 +-
 .../org/apache/maven/plugin/surefire/DataZT2A.java |   8 +-
 .../org/apache/maven/plugin/surefire/DataZT3A.java |   8 +-
 .../maven/plugin/surefire/MojoMocklessTest.java    |  30 +-
 .../surefire/SurefireDependencyResolverTest.java   |  27 +-
 .../maven/plugin/surefire/SurefireHelperTest.java  |   7 +-
 .../plugin/surefire/SurefirePropertiesTest.java    |   4 +-
 ...ooterDeserializerProviderConfigurationTest.java |  54 ++-
 ...BooterDeserializerStartupConfigurationTest.java |  43 +-
 .../booterclient/DefaultForkConfigurationTest.java |  47 ++-
 .../booterclient/ForkConfigurationTest.java        |  26 +-
 .../surefire/booterclient/ForkStarterTest.java     | 272 ++++++++++++
 .../booterclient/ForkingRunListenerTest.java       |   1 +
 .../JarManifestForkConfigurationTest.java          |   3 +-
 .../plugin/surefire/booterclient/MainClass.java    |  36 +-
 .../booterclient/MockNotifiableTestStream.java     |   2 +-
 .../plugin/surefire/booterclient/MockReporter.java |   3 +-
 .../ModularClasspathForkConfigurationTest.java     |  24 +-
 .../OutputStreamFlushableCommandlineTest.java      | 132 ++++++
 .../TestLessInputStreamBuilderTest.java            |  53 ++-
 .../TestProvidingInputStreamTest.java              |   3 +-
 .../booterclient/output/ForkClientTest.java        |  84 ++--
 .../output/ForkedChannelDecoderTest.java           |  27 +-
 .../report/DefaultReporterFactoryTest.java         |  23 +-
 .../surefire/report/StatelessXmlReporterTest.java  |  17 +-
 .../plugin/surefire/report/TestSetStatsTest.java   |   2 +-
 .../surefire/report/WrappedReportEntryTest.java    |  16 +-
 .../runorder/RunEntryStatisticsMapTest.java        |   7 +-
 .../surefire/util/DependenciesScannerTest.java     |  49 ++-
 .../plugin/surefire/util/DirectoryScannerTest.java |  28 +-
 .../org/apache/maven/surefire/JUnit4SuiteTest.java |   2 +
 .../StatelessTestsetInfoReporterTest.java          |   2 +-
 .../report/ConsoleOutputFileReporterTest.java      |   8 +-
 .../maven/surefire/report/FileReporterTest.java    |  13 +-
 .../maven/surefire/report/RunStatisticsTest.java   |  33 +-
 maven-surefire-plugin/pom.xml                      |   4 +-
 .../maven/plugin/surefire/SurefirePlugin.java      |  92 +++-
 .../site/apt/examples/configuring-classpath.apt.vm |   2 +-
 .../src/site/apt/examples/shutdown.apt.vm          |  10 +-
 .../site/apt/examples/skip-after-failure.apt.vm    |   4 +-
 .../src/site/apt/featurematrix.apt.vm              |   5 +-
 maven-surefire-plugin/src/site/apt/index.apt.vm    | 108 +++--
 maven-surefire-plugin/src/site/fml/faq.fml         |  14 +
 .../maven/plugin/surefire/SurefirePluginTest.java  |  32 +-
 maven-surefire-report-plugin/pom.xml               |   2 +-
 .../plugins/surefire/report/JUnit4SuiteTest.java   |   2 +-
 .../plugins/surefire/report/Surefire1183Test.java  |   4 +-
 .../plugins/surefire/report/Surefire597Test.java   |   6 +-
 .../surefire/report/SurefireReportMojoTest.java    |   4 +-
 .../report/SurefireSchemaValidationTest.java       |   5 +-
 .../maven/plugins/surefire/report/Utils.java       |   3 +
 pom.xml                                            |  32 +-
 surefire-api/pom.xml                               |  41 +-
 .../maven/surefire/SpecificTestClassFilter.java    |   2 +-
 .../surefire/booter/ForkedChannelEncoder.java      |   2 +-
 .../maven/surefire/cli/CommandLineOption.java      |  10 -
 .../maven/surefire/testset/ResolvedTest.java       |  14 +-
 .../maven/surefire/testset/TestListResolver.java   |  10 +-
 .../surefire/util/DefaultDirectoryScanner.java     |   4 +-
 .../runorder/ThreadedExecutionSchedulerTest.java   |   1 +
 .../surefire/SpecificTestClassFilterTest.java      |  25 +-
 .../surefire/booter/ForkedChannelEncoderTest.java  |  40 +-
 .../surefire/booter/ForkingRunListenerTest.java    |  38 +-
 .../report/LegacyPojoStackTraceWriterTest.java     | 164 ++++---
 .../apache/maven/surefire/suite/RunResultTest.java |   1 +
 .../surefire/testset/FundamentalFilterTest.java    |   3 +-
 .../maven/surefire/testset/ResolvedTestTest.java   |  14 +-
 .../surefire/testset/TestListResolverTest.java     |  15 +-
 .../maven/surefire/util/ReflectionUtilsTest.java   |   6 +-
 .../surefire/util/RunOrderCalculatorTest.java      |   9 +-
 .../apache/maven/surefire/util/RunOrderTest.java   |   3 +
 .../apache/maven/surefire/util/TestsToRunTest.java |   7 +-
 .../util/internal/ConcurrencyUtilsTest.java        |   4 +-
 .../maven/surefire/util/testdata/DataZT1A.java     |   7 +-
 .../maven/surefire/util/testdata/DataZT2A.java     |   8 +-
 .../maven/surefire/util/testdata/DataZT3A.java     |   8 +-
 .../testdata/java/javascript/DataJavaZT4A.java     |   4 +-
 surefire-booter/pom.xml                            |  37 +-
 .../maven/surefire/booter/BooterConstants.java     |   1 +
 .../maven/surefire/booter/BooterDeserializer.java  |   9 +-
 .../maven/surefire/booter/CommandReader.java       |  30 +-
 .../apache/maven/surefire/booter/ForkedBooter.java |  35 +-
 .../maven/surefire/booter/ModularClasspath.java    |  10 +-
 .../apache/maven/surefire/booter/PpidChecker.java  | 201 ++++++---
 .../maven/surefire/booter/ProcessCheckerType.java  |  66 +--
 .../apache/maven/surefire/booter/ProcessInfo.java  |  18 +-
 .../surefire/booter/StartupConfiguration.java      |  14 +-
 .../apache/maven/surefire/booter/SystemUtils.java  |  12 +-
 .../surefire/booter/BooterDeserializerTest.java    |  57 +++
 .../maven/surefire/booter/ClasspathTest.java       |  12 +-
 .../maven/surefire/booter/CommandReaderTest.java   |  37 +-
 .../DefaultMasterProcessChannelDecoderTest.java    |   5 +-
 .../maven/surefire/booter/ForkedBooterTest.java    |  74 +++-
 .../maven/surefire/booter/JUnit4SuiteTest.java     |   1 +
 .../surefire/booter/NewClassLoaderRunner.java      |   4 +-
 .../maven/surefire/booter/PpidCheckerTest.java     | 199 +++++++--
 .../surefire/booter/PropertiesWrapperTest.java     |  11 +-
 .../surefire/booter/SurefireReflectorTest.java     |  38 +-
 .../maven/surefire/booter/SystemUtilsTest.java     |  19 +-
 surefire-extensions-api/pom.xml                    |  19 +-
 .../extensions/util/CommandlineExecutor.java       | 128 ++++++
 .../extensions/util/CommandlineStreams.java        |  82 ++++
 .../extensions/util/CountdownCloseable.java        |  70 +++
 .../util/FlushableWritableByteChannel.java         |  38 +-
 .../extensions/util/LineConsumerThread.java        | 101 +++++
 .../surefire/extensions/util/StreamFeeder.java     |  89 ++++
 .../extensions/util/CommandlineExecutorTest.java   | 109 +++++
 .../surefire/extensions/util}/JUnit4SuiteTest.java |   9 +-
 surefire-grouper/pom.xml                           |   2 +-
 .../surefire/group/match/AndGroupMatcherTest.java  |   4 +
 .../group/match/InverseGroupMatcherTest.java       |   4 +
 .../surefire/group/match/OrGroupMatcherTest.java   |   4 +
 .../group/match/SingleGroupMatcherTest.java        |   4 +
 .../group/parse/GroupMatcherParserTest.java        |   4 +
 surefire-its/pom.xml                               |   8 +-
 .../maven/surefire/its/AbstractFailFastIT.java     |   7 +
 .../its/AbstractTestMultipleMethodPatterns.java    |  52 ++-
 .../surefire/its/AssumptionFailureReportIT.java    |   5 +
 .../maven/surefire/its/CheckSingleTestIT.java      |   5 +-
 .../surefire/its/CheckTestFailIfNoTestsIT.java     |   5 +-
 .../its/CheckTestNgBeforeMethodFailureIT.java      |   3 +-
 .../surefire/its/CheckTestNgBeforeMethodIT.java    |   3 +-
 .../its/CheckTestNgCustomObjectFactoryIT.java      |   1 +
 .../its/CheckTestNgCustomTestRunnerFactoryIT.java  |   1 +
 .../surefire/its/CheckTestNgExecuteErrorIT.java    |  27 +-
 .../its/CheckTestNgGroupThreadParallelIT.java      |   2 +-
 .../maven/surefire/its/CheckTestNgJdk14IT.java     |   2 +-
 .../its/CheckTestNgListenerReporterIT.java         |  11 +-
 .../maven/surefire/its/CheckTestNgListenersIT.java |   2 +-
 .../surefire/its/CheckTestNgPathWithSpacesIT.java  |   2 +-
 .../surefire/its/CheckTestNgSuiteXmlSingleIT.java  |   2 +-
 .../maven/surefire/its/CheckTestNgVersionsIT.java  |  19 +-
 .../maven/surefire/its/EnvironmentVariablesIT.java |  21 +-
 .../apache/maven/surefire/its/FailFastJUnitIT.java |  10 +-
 .../maven/surefire/its/FailFastTestNgIT.java       |   2 +-
 .../org/apache/maven/surefire/its/ForkModeIT.java  |   4 +-
 .../maven/surefire/its/ForkModeMultiModuleIT.java  |  36 +-
 .../surefire/its/IncludesExcludesFromFileIT.java   |   2 +-
 .../maven/surefire/its/JUnit47ConcurrencyIT.java   |   8 +-
 .../maven/surefire/its/JUnit47ParallelIT.java      | 177 ++++----
 .../surefire/its/JUnit47RedirectOutputIT.java      |   3 +
 .../its/JUnit47RerunFailingTestWithCucumberIT.java |  51 +--
 .../its/JUnit47StaticInnerClassTestsIT.java        |   3 +
 .../maven/surefire/its/JUnit4VersionsIT.java       |   3 +-
 .../org/apache/maven/surefire/its/JUnitDepIT.java  |  29 +-
 .../maven/surefire/its/JUnitPlatformEnginesIT.java | 147 +++++--
 .../apache/maven/surefire/its/JUnitPlatformIT.java |  85 +---
 .../its/JUnitPlatformRerunFailingTestsIT.java      | 293 +++++++++++++
 .../its/JUnitPlatformStreamCorruptionIT.java       |   4 +
 .../apache/maven/surefire/its/JUnitVersion.java    |   3 +-
 .../apache/maven/surefire/its/Java9FullApiIT.java  |   4 +-
 .../apache/maven/surefire/its/ModulePathIT.java    |   3 +
 .../maven/surefire/its/PlexusConflictIT.java       |   2 +-
 .../apache/maven/surefire/its/ReporterTime.java    |   1 +
 .../maven/surefire/its/ResultCountingIT.java       |   1 +
 .../org/apache/maven/surefire/its/RunOrderIT.java  |  86 +++-
 .../surefire/its/RunOrderParallelForksIT.java      |   3 +
 .../maven/surefire/its/SmartStackTraceIT.java      |   6 +-
 .../apache/maven/surefire/its/Surefire1602IT.java  |   1 -
 .../surefire/its/TestNgSuccessPercentageIT.java    |   7 +-
 .../maven/surefire/its/TestSingleMethodIT.java     |   4 +-
 .../apache/maven/surefire/its/TwoTestCasesIT.java  |   5 +-
 .../maven/surefire/its/UnicodeTestNamesIT.java     |   2 +-
 .../maven/surefire/its/WorkingDirectoryIT.java     |   5 +-
 .../its/WorkingDirectoryIsInvalidPropertyIT.java   |   1 +
 .../maven/surefire/its/XmlReporterRunTimeIT.java   |   1 +
 .../its/fixture/FailsafeOutputValidator.java       |   3 +
 .../surefire/its/fixture/HelperAssertions.java     |  11 +-
 .../its/fixture/IntegrationTestSuiteResults.java   |   6 +-
 .../apache/maven/surefire/its/fixture/IsRegex.java |   1 -
 .../maven/surefire/its/fixture/MavenLauncher.java  |  39 +-
 .../surefire/its/fixture/SurefireLauncher.java     |   2 +-
 .../maven/surefire/its/fixture/TestFile.java       |   2 +-
 ...nTestFromDependencyJarsTypeAndClassifierIT.java |  19 +-
 .../Surefire1024VerifyFailsafeIfTestedIT.java      |   3 +-
 ...36NonFilterableJUnitRunnerWithCategoriesIT.java |   4 +-
 .../jiras/Surefire1041FailingJUnitRunnerIT.java    |   5 +-
 .../Surefire1055CorrectConcurrentTestCountIT.java  |   1 +
 .../its/jiras/Surefire1095NpeInRunListener.java    |   2 +-
 .../its/jiras/Surefire1098BalancedRunOrderIT.java  |  41 +-
 ...urefire1135ImproveIgnoreMessageForTestNGIT.java |  67 ++-
 .../its/jiras/Surefire1144XmlRunTimeIT.java        |   3 +-
 .../Surefire1146RerunFailedAndParameterized.java   |   3 +-
 .../Surefire1152RerunFailingTestsInSuiteIT.java    |  13 +-
 .../Surefire1153IncludesAndSpecifiedTestIT.java    |   4 +
 .../its/jiras/Surefire1158RemoveInfoLinesIT.java   |  59 +--
 .../its/jiras/Surefire1209RerunAndForkCountIT.java |  16 +-
 .../its/jiras/Surefire1260NewTestsPattern.java     |   3 +-
 .../Surefire1295AttributeJvmCrashesToTestsIT.java  |   9 +-
 .../its/jiras/Surefire1367AssumptionLogsIT.java    |  99 ++---
 .../Surefire1396CustomProviderClassPathIT.java     |   4 +-
 .../its/jiras/Surefire141PluggableProvidersIT.java |   2 +-
 ...urefire1534ReuseForksFalseWithJavaModuleIT.java |   3 +
 .../jiras/Surefire1535TestNGParallelSuitesIT.java  |  11 +-
 .../maven/surefire/its/jiras/Surefire1585IT.java   |   4 +
 .../its/jiras/Surefire162CharsetProviderIT.java    |   5 +-
 ...refire1712ExtractedModulenameWithoutASMIT.java} |  38 +-
 .../jiras/Surefire224WellFormedXmlFailuresIT.java  |   2 +-
 .../its/jiras/Surefire257NotRerunningTestsIT.java  |  12 +-
 .../jiras/Surefire260TestWithIdenticalNamesIT.java |   5 +-
 .../Surefire569RunTestFromDependencyJarsIT.java    |  21 +-
 .../Surefire570MultipleReportDirectoriesIT.java    |   5 +-
 .../jiras/Surefire613TestCountInParallelIT.java    |   1 +
 .../Surefire649EmptyStringSystemPropertiesIT.java  |  13 +-
 .../jiras/Surefire705ParallelForkTimeoutIT.java    |   8 +-
 ...fire735ForkFailWithRedirectConsoleOutputIT.java |   5 +-
 .../Surefire747MethodParallelWithSuiteCountIT.java |   1 +
 .../its/jiras/Surefire772BothReportsIT.java        |   5 +-
 .../its/jiras/Surefire772NoFailsafeReportsIT.java  |   5 +-
 .../its/jiras/Surefire772SpecifiedReportsIT.java   |   5 +-
 .../its/jiras/Surefire803MultiFailsafeExecsIT.java |  11 +-
 .../jiras/Surefire806SpecifiedTestControlsIT.java  |   4 +
 .../its/jiras/Surefire809GroupExpressionsIT.java   |   4 +
 .../its/jiras/Surefire818NpeIgnoresTestsIT.java    |   7 +-
 .../its/jiras/Surefire828EmptyGroupExprIT.java     |   4 +
 .../its/jiras/Surefire832ProviderSelectionIT.java  |   4 +
 .../jiras/Surefire839TestWithoutCategoriesIT.java  |   4 +
 .../its/jiras/Surefire847AdditionalFailureIT.java  |   4 +
 .../Surefire907PerThreadWithoutThreadCountIT.java  |   4 +
 .../Surefire920TestFailureIgnoreWithTimeoutIT.java |   8 +-
 .../its/jiras/Surefire930TestNgSuiteXmlIT.java     |   4 +
 .../its/jiras/Surefire943ReportContentIT.java      |  14 +-
 ...Surefire946KillMainProcessInReusableForkIT.java |  10 +-
 .../its/jiras/Surefire972BizarreNoClassDefIT.java  |  10 +-
 .../its/jiras/Surefire975DefaultVMEncodingIT.java  |   3 +
 .../its/jiras/Surefire979WrongClassLoaderIT.java   |  13 +-
 ...efire985ParameterizedRunnerAndCategoriesIT.java |   6 +-
 .../src/test/java/forktimeout/BaseForkTimeout.java |   2 +-
 .../src/test/resources/java9-full-api/pom.xml      |  10 +
 .../pom.xml                                        |   9 +-
 .../java/junitplatform}/FlakyFirstTimeTest.java    |  11 +-
 .../src/test/java/junitplatform}/PassingTest.java  |   5 +-
 .../test/java/tags/JUnitPlatformWithTagsTest.java  |   6 +-
 surefire-its/src/test/resources/modulepath/pom.xml |   5 +-
 surefire-its/src/test/resources/pom.xml            |   2 +
 surefire-its/src/test/resources/runOrder/pom.xml   |  34 +-
 .../src/test/resources/surefire-1265/pom.xml       |  13 +-
 .../pom.xml                                        |   9 +-
 .../{junit-platform-tags => surefire-1688}/pom.xml |  38 +-
 .../jira1688/FailingBeforeAllJupiterTest.java}     |  17 +-
 .../pom.xml                                        |  22 +-
 .../src/main/java/module-info.java}                |   5 +-
 .../src/main/java/wtf/g4s8/oot/Foo.java}           |  17 +-
 .../src/test/java/module-info.java                 |   7 +-
 .../src/test/java/wtf/g4s8/oot/test/FooTest.java}  |  15 +-
 .../surefire/selfdestruct/SelfDestructMojo.java    |  11 +-
 .../src/test/resources/system-properties/pom.xml   |   2 +-
 surefire-logger-api/pom.xml                        |   2 +-
 .../maven/plugin/surefire/log/api/LoggersTest.java |   4 +-
 surefire-providers/common-java5/pom.xml            |  32 +-
 .../surefire/report/SmartStackTraceParser.java     |   6 +-
 .../apache/maven/surefire/report/ABaseClass.java   |   4 +-
 .../apache/maven/surefire/report/ASubClass.java    |   3 +
 .../apache/maven/surefire/report/ATestClass.java   |   5 +-
 .../maven/surefire/report/OutermostClass.java      |   3 +-
 .../surefire/report/PojoStackTraceWriterTest.java  |   8 +-
 .../maven/surefire/report/RunnableTestClass1.java  |  37 +-
 .../maven/surefire/report/RunnableTestClass2.java  |  39 +-
 .../surefire/report/SmartStackTraceParserTest.java |  27 +-
 .../surefire/report/StackTraceFocusedOnClass.java  |   2 +-
 .../apache/maven/surefire/report/TestClass1.java   |   1 +
 .../apache/maven/surefire/report/TestClass2.java   |   1 +
 surefire-providers/common-junit3/pom.xml           |   2 +-
 .../common/junit3/JUnit3TestCheckerTest.java       |  28 +-
 surefire-providers/common-junit4/pom.xml           |   2 +-
 .../common/junit4/JUnit4ProviderUtilTest.java      |  12 +-
 .../common/junit4/JUnit4Reflector40Test.java       |   8 +-
 .../common/junit4/JUnit4RunListenerTest.java       |   8 +-
 .../surefire/junit4/JUnit4TestCheckerTest.java     |  36 +-
 surefire-providers/common-junit48/pom.xml          |   2 +-
 .../surefire/common/junit48/FilterFactoryTest.java | 121 ++++--
 .../common/junit48/JUnit48TestCheckerTest.java     |  31 +-
 .../maven/surefire/common/junit48/tests/ATest.java |   4 +
 .../surefire/common/junit48/tests/a/ATest.java     |   4 +
 .../maven/surefire/common/junit48/tests/pt/PT.java |   3 +
 surefire-providers/pom.xml                         |   2 +-
 surefire-providers/surefire-junit-platform/pom.xml |   7 +-
 .../junitplatform/JUnitPlatformProvider.java       |  36 +-
 .../surefire/junitplatform/RunListenerAdapter.java |  50 ++-
 .../junitplatform/JUnitPlatformProviderTest.java   | 395 ++++++++++++++++-
 .../junitplatform/RunListenerAdapterTest.java      |  38 +-
 .../junitplatform/TestMethodFilterTest.java        |   5 +-
 surefire-providers/surefire-junit3/pom.xml         |   2 +-
 .../maven/surefire/junit/JUnitTestSetTest.java     |  12 +
 surefire-providers/surefire-junit4/pom.xml         |   2 +-
 .../maven/surefire/junit4/JUnit4ProviderTest.java  |  14 +-
 surefire-providers/surefire-junit47/pom.xml        |   2 +-
 .../junitcore/ConcurrentRunListenerTest.java       |  26 +-
 .../ConfigurableParallelComputerTest.java          |  19 +-
 .../surefire/junitcore/DiagnosticRunListener.java  |  44 +-
 .../surefire/junitcore/JUnit4Reflector481Test.java |  41 +-
 .../junitcore/JUnitCoreParametersTest.java         |  35 +-
 .../junitcore/JUnitCoreRunListenerTest.java        |  50 ++-
 .../junitcore/MavenSurefireJUnit47RunnerTest.java  |  22 +-
 .../junitcore/MavenSurefireJUnit48RunnerTest.java  |  22 +-
 .../maven/surefire/junitcore/Surefire746Test.java  |  66 ++-
 .../junitcore/Surefire813IncorrectResultTest.java  |   9 +-
 .../maven/surefire/junitcore/TestMethodTest.java   |   5 +-
 .../pc/OptimizedParallelComputerTest.java          | 104 +++--
 .../junitcore/pc/ParallelComputerBuilderTest.java  | 229 ++++++----
 .../junitcore/pc/ParallelComputerUtilTest.java     | 437 +++++++++----------
 .../junitcore/pc/SchedulingStrategiesTest.java     |   2 +-
 surefire-providers/surefire-testng-utils/pom.xml   |   2 +-
 .../test/java/testng/utils/BaseClassSample.java    |   3 +
 .../test/java/testng/utils/ChildClassSample.java   |   3 +
 .../test/java/testng/utils/MethodSelectorTest.java |   3 +
 surefire-providers/surefire-testng/pom.xml         |   2 +-
 .../testng/conf/TestNG513ConfiguratorTest.java     |   3 +
 .../testng/conf/TestNG5141ConfiguratorTest.java    |  36 +-
 .../testng/conf/TestNG5143ConfiguratorTest.java    |  38 +-
 .../testng/conf/TestNGMapConfiguratorTest.java     |  39 +-
 surefire-report-parser/pom.xml                     |  36 +-
 .../plugins/surefire/report/ReportTestCase.java    |   2 +-
 .../surefire/report/SurefireReportParser.java      |   4 +-
 .../surefire/report/TestSuiteXmlParser.java        |   2 +-
 .../surefire/report/ReportTestCaseTest.java        |   1 +
 .../surefire/report/ReportTestSuiteTest.java       |   1 +
 .../surefire/report/SurefireReportParserTest.java  |   1 +
 .../surefire/report/TestSuiteXmlParserTest.java    | 469 ++++++++++-----------
 surefire-shadefire/pom.xml                         |   2 +-
 surefire-shared-utils/pom.xml                      | 113 +++++
 368 files changed, 6602 insertions(+), 2961 deletions(-)
 create mode 100644 .editorconfig
 copy .travis.yml => .github/workflows/maven.yml (59%)
 copy maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/{FlushReceiver.java => OutputStreamFlushReceiver.java} (64%)
 create mode 100644 maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkStarterTest.java
 copy surefire-its/src/test/resources/testng-listener-reporter/src/test/java/listenReport/FileHelper.java => maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/MainClass.java (56%)
 create mode 100644 maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/OutputStreamFlushableCommandlineTest.java
 copy surefire-api/src/main/java/org/apache/maven/surefire/util/internal/ClassMethod.java => surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProcessCheckerType.java (52%)
 create mode 100644 surefire-booter/src/test/java/org/apache/maven/surefire/booter/BooterDeserializerTest.java
 create mode 100644 surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/util/CommandlineExecutor.java
 create mode 100644 surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/util/CommandlineStreams.java
 create mode 100644 surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/util/CountdownCloseable.java
 copy maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/LegacyForkChannel.java => surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/util/FlushableWritableByteChannel.java (52%)
 create mode 100644 surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/util/LineConsumerThread.java
 create mode 100644 surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/util/StreamFeeder.java
 create mode 100644 surefire-extensions-api/src/test/java/org/apache/maven/surefire/extensions/util/CommandlineExecutorTest.java
 copy {surefire-providers/surefire-junit4/src/test/java/org/apache/maven/surefire/junit4 => surefire-extensions-api/src/test/java/org/apache/maven/surefire/extensions/util}/JUnit4SuiteTest.java (81%)
 create mode 100644 surefire-its/src/test/java/org/apache/maven/surefire/its/JUnitPlatformRerunFailingTestsIT.java
 copy surefire-its/src/test/java/org/apache/maven/surefire/its/{ModulePathIT.java => jiras/Surefire1712ExtractedModulenameWithoutASMIT.java} (66%)
 copy surefire-its/src/test/resources/{surefire-1585-jupiter-api => junit-platform-rerun-failing-tests}/pom.xml (89%)
 copy surefire-its/src/test/resources/{junit4-rerun-failing-tests/src/test/java/junit4 => junit-platform-rerun-failing-tests/src/test/java/junitplatform}/FlakyFirstTimeTest.java (88%)
 copy surefire-its/src/test/resources/{junit4-rerun-failing-tests/src/test/java/junit4 => junit-platform-rerun-failing-tests/src/test/java/junitplatform}/PassingTest.java (94%)
 copy surefire-its/src/test/resources/{junit-platform-tags => surefire-1688}/pom.xml (70%)
 copy surefire-its/src/test/resources/{surefire-1585-jupiter-api/src/test/java/JupiterTest.java => surefire-1688/src/test/java/jira1688/FailingBeforeAllJupiterTest.java} (76%)
 copy surefire-its/src/test/resources/{surefire-1585-jupiter-api => surefire-1712-extracted-modulename-without-asm}/pom.xml (72%)
 copy surefire-its/src/test/resources/{surefire-855-failsafe-use-bundle/src/main/java/pkg/AClassInOSGiBundle.java => surefire-1712-extracted-modulename-without-asm/src/main/java/module-info.java} (94%)
 copy surefire-its/src/test/resources/{modulepath/src/test/java/com/app/AppTest.java => surefire-1712-extracted-modulename-without-asm/src/main/java/wtf/g4s8/oot/Foo.java} (81%)
 copy surefire-api/src/test/java/org/apache/maven/surefire/util/testdata/DataZT1A.java => surefire-its/src/test/resources/surefire-1712-extracted-modulename-without-asm/src/test/java/module-info.java (86%)
 copy surefire-its/src/test/resources/{surefire-1585-jupiter-api/src/test/java/JupiterTest.java => surefire-1712-extracted-modulename-without-asm/src/test/java/wtf/g4s8/oot/test/FooTest.java} (77%)
 create mode 100644 surefire-shared-utils/pom.xml


[maven-surefire] 01/01: [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 60c79d69b304cb849a79090ce6f58527e61c309a
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 7604c03..ca87ea8 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.Dependency;
 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.
      *
@@ -2253,6 +2257,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 )
     {
@@ -2262,6 +2274,8 @@ public abstract class AbstractSurefireMojo
 
         Classpath bootClasspath = getArtifactClasspath( shadeFire != null ? shadeFire : surefireBooterArtifact );
 
+        ForkNodeFactory forkNode = getForkNodeFactory();
+
         if ( canExecuteProviderWithModularPath( platform ) )
         {
             return new ModularClasspathForkConfiguration( bootClasspath,
@@ -2276,7 +2290,8 @@ public abstract class AbstractSurefireMojo
                     getEffectiveForkCount(),
                     reuseForks,
                     platform,
-                    getConsoleLogger() );
+                    getConsoleLogger(),
+                    forkNode );
         }
         else if ( getClassLoaderConfiguration().isManifestOnlyJarRequestedAndUsable() )
         {
@@ -2292,7 +2307,8 @@ public abstract class AbstractSurefireMojo
                     getEffectiveForkCount(),
                     reuseForks,
                     platform,
-                    getConsoleLogger() );
+                    getConsoleLogger(),
+                    forkNode );
         }
         else
         {
@@ -2308,7 +2324,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 559a0f1..03cb190 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.Rule;
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 7c38b72..ba748fb 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>
@@ -470,10 +471,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 -->
@@ -489,14 +488,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 c5e5857..7a7e2ba 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.util.internal.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 2e735d9..fb6491e 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.util.internal.StringUtils.isBlank;
 import static org.apache.maven.surefire.util.internal.StringUtils.isNotBlank;
@@ -58,12 +60,10 @@ import static org.apache.maven.surefire.util.internal.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();