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/02/07 00:51:54 UTC

[maven-surefire] branch maven2surefire-jvm-communication updated: finished implementation of extension in ForkStarter (missing JavaDoc in ForkChannel and unit test for NetworkingProcessExecutor - rework)

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


The following commit(s) were added to refs/heads/maven2surefire-jvm-communication by this push:
     new 270160d  finished implementation of extension in ForkStarter (missing JavaDoc in ForkChannel and unit test for NetworkingProcessExecutor - rework)
270160d is described below

commit 270160d0b48ade2f1db289047fe5e48fe8585a48
Author: tibordigana <ti...@apache.org>
AuthorDate: Fri Feb 7 01:51:40 2020 +0100

    finished implementation of extension in ForkStarter (missing JavaDoc in ForkChannel and unit test for NetworkingProcessExecutor - rework)
---
 .../plugin/surefire/AbstractSurefireMojo.java      |   4 +-
 .../plugin/surefire/booterclient/ForkStarter.java  |  40 +++---
 .../output/ThreadedStreamConsumer.java             |   1 +
 .../surefire/extensions/LegacyForkChannel.java     |  79 ++++++++++--
 .../surefire/extensions/LegacyForkNodeFactory.java |   6 +-
 .../extensions/NetworkingProcessExecutor.java      |   4 +-
 .../surefire/extensions/PipeProcessExecutor.java   | 143 ---------------------
 .../surefire/extensions/SurefireForkChannel.java   |  70 +++++++++-
 .../extensions/SurefireForkNodeFactory.java        |   5 +-
 ...NodeFactory.java => CloseableDaemonThread.java} |  21 ++-
 .../surefire/extensions/ExecutableCommandline.java |  40 ------
 .../maven/surefire/extensions/ForkChannel.java     | 123 ++++++++++++++++--
 .../maven/surefire/extensions/ForkNodeFactory.java |   3 +-
 .../extensions/util/LineConsumerThread.java        |   7 +-
 .../surefire/extensions/util/StreamFeeder.java     |   7 +-
 15 files changed, 296 insertions(+), 257 deletions(-)

diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java
index 676b18f..2239921 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
@@ -25,8 +25,8 @@ import org.apache.maven.artifact.DefaultArtifact;
 import org.apache.maven.artifact.handler.ArtifactHandler;
 import org.apache.maven.artifact.repository.ArtifactRepository;
 import org.apache.maven.model.Plugin;
+import org.apache.maven.plugin.surefire.extensions.LegacyForkNodeFactory;
 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;
@@ -2270,7 +2270,7 @@ public abstract class AbstractSurefireMojo
     private ForkNodeFactory getForkNodeFactory()
     {
         ForkNodeFactory forkNode = getForkNode();
-        return forkNode == null ? new SurefireForkNodeFactory() : forkNode;
+        return forkNode == null ? new LegacyForkNodeFactory() : forkNode;
     }
 
     @Nonnull
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 73340a8..8c50bf7 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
@@ -33,11 +33,10 @@ import org.apache.maven.plugin.surefire.booterclient.output.NativeStdErrStreamCo
 import org.apache.maven.plugin.surefire.booterclient.output.ThreadedStreamConsumer;
 import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
 import org.apache.maven.plugin.surefire.report.DefaultReporterFactory;
+import org.apache.maven.surefire.extensions.CloseableDaemonThread;
 import org.apache.maven.surefire.extensions.util.CommandlineExecutor;
 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.booter.AbstractPathConfiguration;
 import org.apache.maven.surefire.booter.PropertiesWrapper;
 import org.apache.maven.surefire.booter.ProviderConfiguration;
@@ -50,6 +49,7 @@ 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.shared.utils.cli.StreamConsumer;
 import org.apache.maven.surefire.suite.RunResult;
 import org.apache.maven.surefire.testset.TestRequest;
 import org.apache.maven.surefire.util.DefaultScanResult;
@@ -568,7 +568,7 @@ public class ForkStarter
         final ForkChannel forkChannel;
         try
         {
-            forkChannel = forkNodeFactory.createForkChannel();
+            forkChannel = forkNodeFactory.createForkChannel( forkNumber );
             tempDir = forkConfiguration.getTempDirectory().getCanonicalPath();
             BooterSerializer booterSerializer = new BooterSerializer( forkConfiguration );
             Long pluginPid = forkConfiguration.getPluginPlatform().getPluginPid();
@@ -619,33 +619,35 @@ public class ForkStarter
         Integer result = null;
         RunResult runResult = null;
         SurefireBooterForkException booterForkException = null;
-        StreamFeeder in = null;
-        LineConsumerThread out = null;
-        LineConsumerThread err = null;
+        CloseableDaemonThread in = null;
+        CloseableDaemonThread out = null;
+        CloseableDaemonThread err = null;
         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,
-            // adaptation of the streams to pipes and sockets
-            // non-default impl may use another classes and not the LineConsumerThread, StreamFeeder - freedom
-            // BEGIN: beginning of the call of the extension
             CommandlineStreams streams = exec.execute();
             closer.addCloseable( streams );
-            in = new StreamFeeder( "std-in-fork-" + forkNumber, streams.getStdInChannel(), commandReader );
+
+            in = forkChannel.useStdIn()
+                ? forkChannel.bindCommandReader( commandReader, streams.getStdInChannel() )
+                : forkChannel.bindCommandReader( commandReader );
             in.start();
-            out = new LineConsumerThread( "std-out-fork-" + forkNumber, streams.getStdOutChannel(),
-                                          eventConsumer, countdownCloseable );
+
+            StreamConsumer stdErrConsumer = new NativeStdErrStreamConsumer( reporter );
+
+            out = forkChannel.useStdOut()
+                ? forkChannel.bindEventHandler( eventConsumer, streams.getStdOutChannel(), countdownCloseable )
+                : forkChannel.bindStdOutConsumer( stdErrConsumer );
             out.start();
-            NativeStdErrStreamConsumer stdErrConsumer = new NativeStdErrStreamConsumer( reporter );
-            err = new LineConsumerThread( "std-err-fork-" + forkNumber, streams.getStdErrChannel(),
-                                          stdErrConsumer, countdownCloseable );
+
+            err = forkChannel.useStdErr()
+                ? forkChannel.bindStdErrConsumer( stdErrConsumer, streams.getStdErrChannel(), countdownCloseable )
+                : forkChannel.bindStdErrConsumer( stdErrConsumer );
             err.start();
+
             result = exec.awaitExit();
-            // END: end of the call of the extension
 
             if ( forkClient.hadTimeout() )
             {
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 b3c5c29..316167c 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
@@ -115,6 +115,7 @@ public final class ThreadedStreamConsumer
     }
 
     @Override
+    // todo remove this method and use object instead of string
     public void handleEvent( @Nonnull String event )
     {
         consumeLine( event );
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/LegacyForkChannel.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/LegacyForkChannel.java
index 28cad5a..c462142 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/LegacyForkChannel.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/LegacyForkChannel.java
@@ -19,32 +19,95 @@ package org.apache.maven.plugin.surefire.extensions;
  * under the License.
  */
 
-import org.apache.maven.surefire.extensions.ExecutableCommandline;
+import org.apache.maven.surefire.extensions.CloseableDaemonThread;
+import org.apache.maven.surefire.extensions.CommandReader;
 import org.apache.maven.surefire.extensions.ForkChannel;
+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.StreamConsumer;
 
 import javax.annotation.Nonnull;
-import java.io.IOException;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.WritableByteChannel;
 
 /**
  *
  */
-final class LegacyForkChannel implements ForkChannel
+final class LegacyForkChannel extends ForkChannel
 {
+    protected LegacyForkChannel( int forkChannelId )
+    {
+        super( forkChannelId );
+    }
+
     @Override
     public String getForkNodeConnectionString()
     {
-        return "pipe://";
+        return "pipe://" + getForkChannelId();
+    }
+
+    @Override
+    public boolean useStdIn()
+    {
+        return true;
+    }
+
+    @Override
+    public boolean useStdOut()
+    {
+        return true;
+    }
+
+    @Override
+    public boolean useStdErr()
+    {
+        return true;
+    }
+
+    @Override
+    public CloseableDaemonThread bindCommandReader( @Nonnull CommandReader commands,
+                                                    @Nonnull WritableByteChannel stdIn )
+    {
+        return new StreamFeeder( "std-in-fork-" + getForkChannelId(), stdIn, commands );
+    }
+
+    @Override
+    public CloseableDaemonThread bindCommandReader( @Nonnull CommandReader commands )
+    {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public CloseableDaemonThread bindEventHandler( @Nonnull StreamConsumer consumer,
+                                                   @Nonnull ReadableByteChannel stdOut,
+                                                   @Nonnull CountdownCloseable countdownCloseable )
+    {
+        return new LineConsumerThread( "std-out-fork-" + getForkChannelId(), stdOut, consumer, countdownCloseable );
+    }
+
+    @Override
+    public CloseableDaemonThread bindStdOutConsumer( @Nonnull StreamConsumer consumer )
+    {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public CloseableDaemonThread bindStdErrConsumer(
+        @Nonnull StreamConsumer consumer,
+        @Nonnull ReadableByteChannel stdErr, @Nonnull CountdownCloseable countdownCloseable )
+    {
+        return new LineConsumerThread( "std-err-fork-" + getForkChannelId(), stdErr, consumer, countdownCloseable );
     }
 
-    @Nonnull
     @Override
-    public ExecutableCommandline createExecutableCommandline() throws IOException
+    public CloseableDaemonThread bindStdErrConsumer( @Nonnull StreamConsumer consumer )
     {
-        return new PipeProcessExecutor();
+        throw new UnsupportedOperationException();
     }
 
     @Override
-    public void close() throws IOException
+    public void close()
     {
     }
 }
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/LegacyForkNodeFactory.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/LegacyForkNodeFactory.java
index 7231d00..836464e 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/LegacyForkNodeFactory.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/LegacyForkNodeFactory.java
@@ -22,8 +22,8 @@ package org.apache.maven.plugin.surefire.extensions;
 import org.apache.maven.surefire.extensions.ForkChannel;
 import org.apache.maven.surefire.extensions.ForkNodeFactory;
 
+import javax.annotation.Nonnegative;
 import javax.annotation.Nonnull;
-import java.io.IOException;
 
 /**
  *
@@ -32,8 +32,8 @@ public class LegacyForkNodeFactory implements ForkNodeFactory
 {
     @Nonnull
     @Override
-    public ForkChannel createForkChannel() throws IOException
+    public ForkChannel createForkChannel( @Nonnegative int forkChannelId )
     {
-        return new LegacyForkChannel();
+        return new LegacyForkChannel( forkChannelId );
     }
 }
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/NetworkingProcessExecutor.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/NetworkingProcessExecutor.java
index eecb5ca..1164ba8 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/NetworkingProcessExecutor.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/NetworkingProcessExecutor.java
@@ -23,7 +23,6 @@ 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;
@@ -48,7 +47,7 @@ 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
+final class NetworkingProcessExecutor
 {
     private final AsynchronousServerSocketChannel server;
     private final ExecutorService executorService;
@@ -60,7 +59,6 @@ final class NetworkingProcessExecutor implements ExecutableCommandline
     }
 
     @Nonnull
-    @Override
     public Callable<Integer> executeCommandLineAsCallable( @Nonnull Commandline cli,
                                                            @Nonnull final CommandReader commands,
                                                            @Nonnull final EventHandler events,
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
deleted file mode 100644
index ad3bcfc..0000000
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/PipeProcessExecutor.java
+++ /dev/null
@@ -1,143 +0,0 @@
-package org.apache.maven.plugin.surefire.extensions;
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import org.apache.maven.surefire.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 Callable<Integer> executeCommandLineAsCallable( @Nonnull Commandline cli,
-                                                           @Nonnull CommandReader commands,
-                                                           @Nonnull EventHandler events,
-                                                           StdOutStreamLine stdOut,
-                                                           StdErrStreamLine stdErr,
-                                                           @Nonnull Runnable runAfterProcessTermination )
-            throws Exception
-    {
-        return CommandLineUtils.executeCommandLineAsCallable( cli, new CommandReaderAdapter( commands ),
-                new EventHandlerAdapter( events ), 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/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireForkChannel.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireForkChannel.java
index 2238898..e1932a9 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireForkChannel.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireForkChannel.java
@@ -19,14 +19,19 @@ package org.apache.maven.plugin.surefire.extensions;
  * under the License.
  */
 
-import org.apache.maven.surefire.extensions.ExecutableCommandline;
+import org.apache.maven.surefire.extensions.CloseableDaemonThread;
+import org.apache.maven.surefire.extensions.CommandReader;
 import org.apache.maven.surefire.extensions.ForkChannel;
+import org.apache.maven.surefire.extensions.util.CountdownCloseable;
+import org.apache.maven.surefire.shared.utils.cli.StreamConsumer;
 
 import javax.annotation.Nonnull;
 import java.io.IOException;
 import java.net.InetSocketAddress;
 import java.net.SocketOption;
 import java.nio.channels.AsynchronousServerSocketChannel;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.WritableByteChannel;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 
@@ -40,14 +45,15 @@ import static org.apache.maven.surefire.util.internal.DaemonThreadFactory.newDae
 /**
  *
  */
-final class SurefireForkChannel implements ForkChannel
+final class SurefireForkChannel extends ForkChannel
 {
     private final ExecutorService executorService;
     private final AsynchronousServerSocketChannel server;
     private final int serverPort;
 
-    SurefireForkChannel() throws IOException
+    SurefireForkChannel( int forkChannelId ) throws IOException
     {
+        super( forkChannelId );
         executorService = Executors.newCachedThreadPool( newDaemonThreadFactory() );
         server = open( withThreadPool( executorService ) );
         setTrueOptions( SO_REUSEADDR, TCP_NODELAY, SO_KEEPALIVE );
@@ -73,11 +79,63 @@ final class SurefireForkChannel implements ForkChannel
         return "tcp://127.0.0.1:" + serverPort;
     }
 
-    @Nonnull
     @Override
-    public ExecutableCommandline createExecutableCommandline()
+    public boolean useStdIn()
     {
-        return new NetworkingProcessExecutor( server, executorService );
+        return false;
+    }
+
+    @Override
+    public boolean useStdOut()
+    {
+        return false;
+    }
+
+    @Override
+    public boolean useStdErr()
+    {
+        return false;
+    }
+
+    @Override
+    public CloseableDaemonThread bindCommandReader( @Nonnull CommandReader commands,
+                                                    @Nonnull WritableByteChannel stdIn )
+    {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public CloseableDaemonThread bindCommandReader( @Nonnull CommandReader commands ) throws IOException
+    {
+        return null;
+    }
+
+    @Override
+    public CloseableDaemonThread bindEventHandler( @Nonnull StreamConsumer consumer,
+                                                   @Nonnull ReadableByteChannel stdOut,
+                                                   @Nonnull CountdownCloseable countdownCloseable )
+    {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public CloseableDaemonThread bindStdOutConsumer( @Nonnull StreamConsumer consumer ) throws IOException
+    {
+        return null;
+    }
+
+    @Override
+    public CloseableDaemonThread bindStdErrConsumer( @Nonnull StreamConsumer consumer,
+                                                     @Nonnull ReadableByteChannel stdErr,
+                                                     @Nonnull CountdownCloseable countdownCloseable )
+    {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public CloseableDaemonThread bindStdErrConsumer( @Nonnull StreamConsumer consumer ) throws IOException
+    {
+        return null;
     }
 
     @Override
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireForkNodeFactory.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireForkNodeFactory.java
index c483619..c076ba2 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireForkNodeFactory.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireForkNodeFactory.java
@@ -22,6 +22,7 @@ package org.apache.maven.plugin.surefire.extensions;
 import org.apache.maven.surefire.extensions.ForkChannel;
 import org.apache.maven.surefire.extensions.ForkNodeFactory;
 
+import javax.annotation.Nonnegative;
 import javax.annotation.Nonnull;
 import java.io.IOException;
 
@@ -32,8 +33,8 @@ public class SurefireForkNodeFactory implements ForkNodeFactory
 {
     @Nonnull
     @Override
-    public ForkChannel createForkChannel() throws IOException
+    public ForkChannel createForkChannel( @Nonnegative int forkChannelId ) throws IOException
     {
-        return new SurefireForkChannel();
+        return new SurefireForkChannel( forkChannelId );
     }
 }
diff --git a/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/ForkNodeFactory.java b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/CloseableDaemonThread.java
similarity index 69%
copy from surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/ForkNodeFactory.java
copy to surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/CloseableDaemonThread.java
index c054776..a15ed8a 100644
--- a/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/ForkNodeFactory.java
+++ b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/CloseableDaemonThread.java
@@ -20,19 +20,18 @@ package org.apache.maven.surefire.extensions;
  */
 
 import javax.annotation.Nonnull;
-import java.io.IOException;
+import java.io.Closeable;
 
 /**
- * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
- * @since 3.0.0-M4
+ * The base thread class used to handle a stream and transform it to an object.
  */
-public interface ForkNodeFactory
+public abstract class CloseableDaemonThread extends Thread implements Closeable
 {
-    /**
-     * Opens and closes the channel.
-     *
-     * @return specific implementation of the communication channel
-     * @throws IOException if cannot open the channel
-     */
-    @Nonnull ForkChannel createForkChannel() throws IOException;
+    protected CloseableDaemonThread( @Nonnull String threadName )
+    {
+        setName( threadName );
+        setDaemon( true );
+    }
+
+    public abstract void disable();
 }
diff --git a/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/ExecutableCommandline.java b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/ExecutableCommandline.java
deleted file mode 100644
index ed7a8ec..0000000
--- a/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/ExecutableCommandline.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package org.apache.maven.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.shared.utils.cli.Commandline;
-
-import javax.annotation.Nonnull;
-import java.util.concurrent.Callable;
-
-/**
- * todo add javadoc
- */
-public interface ExecutableCommandline
-{
-    @Nonnull
-    Callable<Integer> executeCommandLineAsCallable( @Nonnull Commandline cli,
-                                                    @Nonnull CommandReader commands,
-                                                    @Nonnull EventHandler events,
-                                                    StdOutStreamLine stdOut,
-                                                    StdErrStreamLine stdErr,
-                                                    @Nonnull Runnable runAfterProcessTermination )
-            throws Exception;
-}
diff --git a/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/ForkChannel.java b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/ForkChannel.java
index 62ef9bf..da19dfc 100644
--- a/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/ForkChannel.java
+++ b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/ForkChannel.java
@@ -19,24 +19,125 @@ package org.apache.maven.surefire.extensions;
  * under the License.
  */
 
+import org.apache.maven.surefire.extensions.util.CountdownCloseable;
+import org.apache.maven.surefire.shared.utils.cli.StreamConsumer;
+
 import javax.annotation.Nonnull;
 import java.io.Closeable;
 import java.io.IOException;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.WritableByteChannel;
 
 /**
- * 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.
+ * The constructor prepares I/O or throws {@link IOException}.
  */
-public interface ForkChannel extends Closeable
+public abstract class ForkChannel implements Closeable
 {
-    String getForkNodeConnectionString();
+    private final int forkChannelId;
+
+    /**
+     *
+     * @param forkChannelId
+     */
+    protected ForkChannel( int forkChannelId )
+    {
+        this.forkChannelId = forkChannelId;
+    }
+
+    /**
+     *
+     * @return
+     */
+    public abstract String getForkNodeConnectionString();
+
+    /**
+     *
+     * @return
+     */
+    public abstract boolean useStdIn();
+
+    /**
+     *
+     * @return
+     */
+    public abstract boolean useStdOut();
+
+    /**
+     *
+     * @return
+     */
+    public abstract boolean useStdErr();
+
+    /**
+     *
+     * @param commands
+     * @param stdIn
+     * @return
+     * @throws IOException
+     */
+    public abstract CloseableDaemonThread bindCommandReader( @Nonnull CommandReader commands,
+                                                             @Nonnull WritableByteChannel stdIn )
+        throws IOException;
+
+    /**
+     *
+     * @param commands
+     * @return
+     * @throws IOException
+     */
+    public abstract CloseableDaemonThread bindCommandReader( @Nonnull CommandReader commands )
+        throws IOException;
+
+    /**
+     *
+     * @param consumer
+     * @param stdOut
+     * @param countdownCloseable
+     * @return
+     * @throws IOException
+     */
+    public abstract CloseableDaemonThread bindEventHandler( @Nonnull StreamConsumer consumer,
+                                                            @Nonnull ReadableByteChannel stdOut,
+                                                            @Nonnull CountdownCloseable countdownCloseable )
+        throws IOException;
+
+    /**
+     *
+     * @param consumer
+     * @return
+     * @throws IOException
+     */
+    public abstract CloseableDaemonThread bindStdOutConsumer( @Nonnull StreamConsumer consumer )
+        throws IOException;
+
+    /**
+     *
+     * @param consumer
+     * @param stdErr
+     * @param countdownCloseable
+     * @return
+     * @throws IOException
+     */
+    public abstract CloseableDaemonThread bindStdErrConsumer( @Nonnull StreamConsumer consumer,
+                                                              @Nonnull ReadableByteChannel stdErr,
+                                                              @Nonnull CountdownCloseable countdownCloseable )
+        throws IOException;
 
-    @Nonnull
-    ExecutableCommandline createExecutableCommandline() throws IOException;
+    /**
+     *
+     * @param consumer
+     * @return
+     * @throws IOException
+     */
+    public abstract CloseableDaemonThread bindStdErrConsumer( @Nonnull StreamConsumer consumer )
+        throws IOException;
 
-    @Override
-    void close() throws IOException;
+    /**
+     *
+     * @return
+     */
+    protected final int getForkChannelId()
+    {
+        return forkChannelId;
+    }
 }
diff --git a/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/ForkNodeFactory.java b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/ForkNodeFactory.java
index c054776..3e0d6a6 100644
--- a/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/ForkNodeFactory.java
+++ b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/ForkNodeFactory.java
@@ -19,6 +19,7 @@ package org.apache.maven.surefire.extensions;
  * under the License.
  */
 
+import javax.annotation.Nonnegative;
 import javax.annotation.Nonnull;
 import java.io.IOException;
 
@@ -34,5 +35,5 @@ public interface ForkNodeFactory
      * @return specific implementation of the communication channel
      * @throws IOException if cannot open the channel
      */
-    @Nonnull ForkChannel createForkChannel() throws IOException;
+    @Nonnull ForkChannel createForkChannel( @Nonnegative int forkChannelId ) throws IOException;
 }
diff --git a/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/util/LineConsumerThread.java b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/util/LineConsumerThread.java
index 109e541..162af4b 100644
--- a/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/util/LineConsumerThread.java
+++ b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/util/LineConsumerThread.java
@@ -19,10 +19,10 @@ package org.apache.maven.surefire.extensions.util;
  * under the License.
  */
 
+import org.apache.maven.surefire.extensions.CloseableDaemonThread;
 import org.apache.maven.surefire.shared.utils.cli.StreamConsumer;
 
 import javax.annotation.Nonnull;
-import java.io.Closeable;
 import java.io.IOException;
 import java.nio.channels.ReadableByteChannel;
 import java.nio.charset.Charset;
@@ -31,7 +31,7 @@ import java.util.Scanner;
 /**
  *
  */
-public final class LineConsumerThread extends Thread implements Closeable
+public final class LineConsumerThread extends CloseableDaemonThread
 {
     private final Charset encoding;
     private final ReadableByteChannel channel;
@@ -50,8 +50,7 @@ public final class LineConsumerThread extends Thread implements Closeable
                                @Nonnull ReadableByteChannel channel, @Nonnull StreamConsumer consumer,
                                @Nonnull CountdownCloseable countdownCloseable, @Nonnull Charset encoding )
     {
-        setName( threadName );
-        setDaemon( true );
+        super( threadName );
         this.channel = channel;
         this.consumer = consumer;
         this.countdownCloseable = countdownCloseable;
diff --git a/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/util/StreamFeeder.java b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/util/StreamFeeder.java
index 9305d07..78e5ce0 100644
--- a/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/util/StreamFeeder.java
+++ b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/util/StreamFeeder.java
@@ -21,10 +21,10 @@ package org.apache.maven.surefire.extensions.util;
 
 import org.apache.maven.surefire.booter.Command;
 import org.apache.maven.surefire.booter.MasterProcessCommand;
+import org.apache.maven.surefire.extensions.CloseableDaemonThread;
 import org.apache.maven.surefire.extensions.CommandReader;
 
 import javax.annotation.Nonnull;
-import java.io.Closeable;
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.channels.ClosedChannelException;
@@ -34,7 +34,7 @@ import java.nio.channels.WritableByteChannel;
 /**
  *
  */
-public class StreamFeeder extends Thread implements Closeable
+public class StreamFeeder extends CloseableDaemonThread
 {
     private final WritableByteChannel channel;
     private final CommandReader commandReader;
@@ -45,8 +45,7 @@ public class StreamFeeder extends Thread implements Closeable
     public StreamFeeder( @Nonnull String threadName, @Nonnull WritableByteChannel channel,
                          @Nonnull CommandReader commandReader )
     {
-        setName( threadName );
-        setDaemon( true );
+        super( threadName );
         this.channel = channel;
         this.commandReader = commandReader;
     }