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/06/04 22:28:32 UTC
[maven-surefire] 01/01: [SUREFIRE-1796] The session of TCP channel
should be authenticated
This is an automated email from the ASF dual-hosted git repository.
tibordigana pushed a commit to branch SUREFIRE-1796
in repository https://gitbox.apache.org/repos/asf/maven-surefire.git
commit 8559694ca073e07fdad34ae917aaaa4c463817f5
Author: tibordigana <ti...@apache.org>
AuthorDate: Fri Jun 5 00:28:20 2020 +0200
[SUREFIRE-1796] The session of TCP channel should be authenticated
---
.../plugin/surefire/booterclient/ForkStarter.java | 16 ++-
.../surefire/extensions/SurefireForkChannel.java | 32 +++++-
.../maven/plugin/surefire/extensions/E2ETest.java | 84 +++++++++++++++-
.../maven/surefire/extensions/ForkChannelTest.java | 19 +++-
...refireMasterProcessChannelProcessorFactory.java | 29 ++++++
.../surefire/booter/ForkedBooterMockTest.java | 111 ++++++++++++++++++++-
.../surefire/extensions/ForkNodeArguments.java | 4 +
7 files changed, 285 insertions(+), 10 deletions(-)
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 3a8cd4f..19b6562 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
@@ -65,6 +65,7 @@ import java.util.Collection;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
+import java.util.UUID;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentLinkedQueue;
@@ -83,6 +84,7 @@ 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.UUID.randomUUID;
import static java.util.concurrent.Executors.newScheduledThreadPool;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.SECONDS;
@@ -573,7 +575,8 @@ public class ForkStarter
File dumpLogDir = replaceForkThreadsInPath( startupReportConfiguration.getReportsDirectory(), forkNumber );
try
{
- forkChannel = forkNodeFactory.createForkChannel( new ForkedNodeArg( forkNumber, dumpLogDir ) );
+ ForkNodeArguments forkNodeArguments = new ForkedNodeArg( forkNumber, dumpLogDir, randomUUID() );
+ forkChannel = forkNodeFactory.createForkChannel( forkNodeArguments );
closer.addCloseable( forkChannel );
tempDir = forkConfiguration.getTempDirectory().getCanonicalPath();
BooterSerializer booterSerializer = new BooterSerializer( forkConfiguration );
@@ -881,11 +884,20 @@ public class ForkStarter
{
private final int forkChannelId;
private final File dumpLogDir;
+ private final UUID sessionId;
- ForkedNodeArg( int forkChannelId, File dumpLogDir )
+ ForkedNodeArg( int forkChannelId, File dumpLogDir, UUID sessionId )
{
this.forkChannelId = forkChannelId;
this.dumpLogDir = dumpLogDir;
+ this.sessionId = sessionId;
+ }
+
+ @Nonnull
+ @Override
+ public UUID getSessionId()
+ {
+ return sessionId;
}
@Override
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 3ca2dad..8141e1b 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
@@ -36,10 +36,12 @@ import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketOption;
+import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
+import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -49,6 +51,7 @@ 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 java.nio.charset.StandardCharsets.US_ASCII;
import static org.apache.maven.surefire.api.util.internal.Channels.newBufferedChannel;
import static org.apache.maven.surefire.api.util.internal.Channels.newChannel;
import static org.apache.maven.surefire.api.util.internal.Channels.newInputStream;
@@ -71,11 +74,14 @@ import static org.apache.maven.surefire.api.util.internal.DaemonThreadFactory.ne
*/
final class SurefireForkChannel extends ForkChannel
{
+ private static final int UUID_STRING_LENGTH = 36;
+
private static final ExecutorService THREAD_POOL = Executors.newCachedThreadPool( newDaemonThreadFactory() );
private final AsynchronousServerSocketChannel server;
private final String localHost;
private final int localPort;
+ private final UUID sessionId;
private volatile AsynchronousSocketChannel worker;
private volatile LineConsumerThread out;
@@ -89,6 +95,7 @@ final class SurefireForkChannel extends ForkChannel
InetSocketAddress localAddress = (InetSocketAddress) server.getLocalAddress();
localHost = localAddress.getHostString();
localPort = localAddress.getPort();
+ sessionId = arguments.getSessionId();
}
@Override
@@ -102,6 +109,10 @@ final class SurefireForkChannel extends ForkChannel
try
{
worker = server.accept().get();
+ if ( sessionId != null )
+ {
+ authenticate();
+ }
}
catch ( InterruptedException e )
{
@@ -113,6 +124,25 @@ final class SurefireForkChannel extends ForkChannel
}
}
+ private void authenticate() throws InterruptedException, ExecutionException, IOException
+ {
+ ByteBuffer hash = ByteBuffer.allocate( UUID_STRING_LENGTH );
+ int read;
+ do
+ {
+ read = worker.read( hash ).get();
+ } while ( read != -1 && hash.hasRemaining() );
+ if ( read == -1 )
+ {
+ throw new IOException( "Channel closed while authenticating the client." );
+ }
+ hash.flip();
+ if ( !UUID.fromString( new String( hash.array(), US_ASCII ) ).equals( sessionId ) )
+ {
+ throw new IOException( "authentication failed" );
+ }
+ }
+
@SafeVarargs
private final void setTrueOptions( SocketOption<Boolean>... options )
throws IOException
@@ -129,7 +159,7 @@ final class SurefireForkChannel extends ForkChannel
@Override
public String getForkNodeConnectionString()
{
- return "tcp://" + localHost + ":" + localPort;
+ return "tcp://" + localHost + ":" + localPort + ( sessionId == null ? "" : "?auth=" + sessionId );
}
@Override
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/extensions/E2ETest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/extensions/E2ETest.java
index 30517f3..02bd68f 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/extensions/E2ETest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/extensions/E2ETest.java
@@ -21,18 +21,23 @@ package org.apache.maven.plugin.surefire.extensions;
import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
import org.apache.maven.surefire.api.booter.MasterProcessChannelEncoder;
-import org.apache.maven.surefire.booter.spi.SurefireMasterProcessChannelProcessorFactory;
import org.apache.maven.surefire.api.event.Event;
+import org.apache.maven.surefire.api.report.ConsoleOutputReceiver;
+import org.apache.maven.surefire.booter.spi.SurefireMasterProcessChannelProcessorFactory;
import org.apache.maven.surefire.extensions.EventHandler;
import org.apache.maven.surefire.extensions.ForkNodeArguments;
import org.apache.maven.surefire.extensions.util.CountdownCloseable;
-import org.apache.maven.surefire.api.report.ConsoleOutputReceiver;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.ExpectedException;
import javax.annotation.Nonnull;
import java.io.Closeable;
+import java.io.IOException;
+import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
+import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
@@ -52,8 +57,11 @@ public class E2ETest
private static final String LONG_STRING =
"0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789";
+ @Rule
+ public final ExpectedException e = ExpectedException.none();
+
@Test
- public void test() throws Exception
+ public void endToEndTest() throws Exception
{
ConsoleLogger logger = mock( ConsoleLogger.class );
ForkNodeArguments arguments = mock( ForkNodeArguments.class );
@@ -184,4 +192,74 @@ public class E2ETest
.isPositive()
.isLessThanOrEqualTo( 6_000L );
}
+
+ @Test( timeout = 10_000L )
+ public void shouldAuthenticateClient() throws Exception
+ {
+ ForkNodeArguments forkNodeArguments = mock( ForkNodeArguments.class );
+ when( forkNodeArguments.getSessionId() ).thenReturn( UUID.randomUUID() );
+
+ try ( SurefireForkChannel server = new SurefireForkChannel( forkNodeArguments ) )
+ {
+ Thread t = new Thread()
+ {
+ @Override
+ public void run()
+ {
+ SurefireMasterProcessChannelProcessorFactory client
+ = new SurefireMasterProcessChannelProcessorFactory();
+ try
+ {
+ client.connect( server.getForkNodeConnectionString() );
+ }
+ catch ( IOException e )
+ {
+ e.printStackTrace();
+ throw new RuntimeException( e );
+ }
+ }
+ };
+ t.setDaemon( true );
+ t.start();
+ server.connectToClient();
+ }
+ }
+
+ @Test( timeout = 10_000L )
+ public void shouldNotAuthenticateClient() throws Exception
+ {
+ ForkNodeArguments forkNodeArguments = mock( ForkNodeArguments.class );
+ when( forkNodeArguments.getSessionId() ).thenReturn( UUID.randomUUID() );
+
+ try ( SurefireForkChannel server = new SurefireForkChannel( forkNodeArguments ) )
+ {
+ Thread t = new Thread()
+ {
+ @Override
+ public void run()
+ {
+ SurefireMasterProcessChannelProcessorFactory client
+ = new SurefireMasterProcessChannelProcessorFactory();
+ try
+ {
+ URI connectionString = new URI( server.getForkNodeConnectionString() );
+ client.connect( "tcp://127.0.0.1:" + connectionString.getPort()
+ + "?auth=6ba7b812-9dad-11d1-80b4-00c04fd430c8" );
+ }
+ catch ( Exception e )
+ {
+ e.printStackTrace();
+ throw new RuntimeException( e );
+ }
+ }
+ };
+ t.setDaemon( true );
+ t.start();
+
+ e.expect( IOException.class );
+ e.expectMessage( "authentication failed" );
+
+ server.connectToClient();
+ }
+ }
}
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/surefire/extensions/ForkChannelTest.java b/maven-surefire-common/src/test/java/org/apache/maven/surefire/extensions/ForkChannelTest.java
index ac30a66..a215c80 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/surefire/extensions/ForkChannelTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/surefire/extensions/ForkChannelTest.java
@@ -38,6 +38,7 @@ import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import java.util.Queue;
+import java.util.UUID;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -61,8 +62,16 @@ public class ForkChannelTest
@Test( timeout = TESTCASE_TIMEOUT )
public void shouldRequestReplyMessagesViaTCP() throws Exception
{
+ final UUID sessionId = UUID.randomUUID();
ForkNodeArguments forkNodeArguments = new ForkNodeArguments()
{
+ @Nonnull
+ @Override
+ public UUID getSessionId()
+ {
+ return sessionId;
+ }
+
@Override
public int getForkChannelId()
{
@@ -100,7 +109,8 @@ public class ForkChannelTest
assertThat( channel.getForkNodeConnectionString() )
.startsWith( "tcp://127.0.0.1:" )
- .isNotEqualTo( "tcp://127.0.0.1:" );
+ .isNotEqualTo( "tcp://127.0.0.1:" )
+ .endsWith( "?auth=" + sessionId );
URI uri = new URI( channel.getForkNodeConnectionString() );
@@ -121,7 +131,7 @@ public class ForkChannelTest
CountdownCloseable cc = new CountdownCloseable( closeable, 2 );
Consumer consumer = new Consumer();
- Client client = new Client( uri.getPort() );
+ Client client = new Client( uri.getPort(), sessionId.toString() );
client.start();
channel.connectToClient();
@@ -162,10 +172,12 @@ public class ForkChannelTest
private final class Client extends Thread
{
private final int port;
+ private final String sessionId;
- private Client( int port )
+ private Client( int port, String sessionId )
{
this.port = port;
+ this.sessionId = sessionId;
}
@Override
@@ -173,6 +185,7 @@ public class ForkChannelTest
{
try ( Socket socket = new Socket( "127.0.0.1", port ) )
{
+ socket.getOutputStream().write( sessionId.getBytes( US_ASCII ) );
byte[] data = new byte[128];
int readLength = socket.getInputStream().read( data );
String token = new String( data, 0, readLength, US_ASCII );
diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/SurefireMasterProcessChannelProcessorFactory.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/SurefireMasterProcessChannelProcessorFactory.java
index f27aa0c..2da3175 100644
--- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/SurefireMasterProcessChannelProcessorFactory.java
+++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/SurefireMasterProcessChannelProcessorFactory.java
@@ -29,7 +29,10 @@ import java.net.MalformedURLException;
import java.net.SocketOption;
import java.net.URI;
import java.net.URISyntaxException;
+import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
+import java.util.StringTokenizer;
+import java.util.UUID;
import java.util.concurrent.ExecutionException;
import static java.net.StandardSocketOptions.SO_KEEPALIVE;
@@ -37,6 +40,7 @@ import static java.net.StandardSocketOptions.SO_REUSEADDR;
import static java.net.StandardSocketOptions.TCP_NODELAY;
import static java.nio.channels.AsynchronousChannelGroup.withFixedThreadPool;
import static java.nio.channels.AsynchronousSocketChannel.open;
+import static java.nio.charset.StandardCharsets.US_ASCII;
import static org.apache.maven.surefire.api.util.internal.Channels.newBufferedChannel;
import static org.apache.maven.surefire.api.util.internal.Channels.newInputStream;
import static org.apache.maven.surefire.api.util.internal.Channels.newOutputStream;
@@ -75,6 +79,12 @@ public class SurefireMasterProcessChannelProcessorFactory
clientSocketChannel = open( withFixedThreadPool( 2, newDaemonThreadFactory() ) );
setTrueOptions( SO_REUSEADDR, TCP_NODELAY, SO_KEEPALIVE );
clientSocketChannel.connect( hostAddress ).get();
+ UUID auth = parseAuth( uri );
+ if ( auth != null )
+ {
+ ByteBuffer buff = ByteBuffer.wrap( auth.toString().getBytes( US_ASCII ) );
+ clientSocketChannel.write( buff );
+ }
}
catch ( URISyntaxException | InterruptedException e )
{
@@ -119,4 +129,23 @@ public class SurefireMasterProcessChannelProcessorFactory
}
}
}
+
+ private UUID parseAuth( URI uri )
+ {
+ String query = uri.getQuery();
+ if ( query == null )
+ {
+ return null;
+ }
+ for ( StringTokenizer tokenizer = new StringTokenizer( query, "&" ); tokenizer.hasMoreTokens(); )
+ {
+ String token = tokenizer.nextToken();
+ int delimiter = token.indexOf( '=' );
+ if ( delimiter != -1 && "auth".equals( token.substring( 0, delimiter ) ) )
+ {
+ return UUID.fromString( token.substring( delimiter + 1 ) );
+ }
+ }
+ return null;
+ }
}
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 11a3df2..a3e90a3 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
@@ -21,11 +21,11 @@ package org.apache.maven.surefire.booter;
import org.apache.maven.surefire.api.booter.MasterProcessChannelDecoder;
import org.apache.maven.surefire.api.booter.MasterProcessChannelEncoder;
+import org.apache.maven.surefire.api.report.StackTraceWriter;
import org.apache.maven.surefire.booter.spi.LegacyMasterProcessChannelDecoder;
import org.apache.maven.surefire.booter.spi.LegacyMasterProcessChannelEncoder;
import org.apache.maven.surefire.booter.spi.LegacyMasterProcessChannelProcessorFactory;
import org.apache.maven.surefire.booter.spi.SurefireMasterProcessChannelProcessorFactory;
-import org.apache.maven.surefire.api.report.StackTraceWriter;
import org.apache.maven.surefire.shared.utils.cli.ShutdownHookUtils;
import org.apache.maven.surefire.spi.MasterProcessChannelProcessorFactory;
import org.junit.Rule;
@@ -45,11 +45,19 @@ import org.powermock.modules.junit4.PowerMockRunner;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
+import java.net.URI;
+import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
+import java.nio.channels.SocketChannel;
+import java.util.UUID;
+import java.util.concurrent.Callable;
+import java.util.concurrent.FutureTask;
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.charset.StandardCharsets.US_ASCII;
+import static java.util.concurrent.TimeUnit.SECONDS;
import static org.fest.assertions.Assertions.assertThat;
import static org.fest.assertions.Fail.fail;
import static org.mockito.ArgumentMatchers.any;
@@ -83,6 +91,8 @@ import static org.powermock.reflect.Whitebox.setInternalState;
@PowerMockIgnore( { "org.jacoco.agent.rt.*", "com.vladium.emma.rt.*" } )
public class ForkedBooterMockTest
{
+ private static final int UUID_STRING_LENGTH = 36;
+
@Rule
public final ErrorCollector errorCollector = new ErrorCollector();
@@ -363,6 +373,83 @@ public class ForkedBooterMockTest
}
@Test
+ public void shouldAuthenticate() throws Exception
+ {
+ mockStatic( ForkedBooter.class );
+
+ doCallRealMethod()
+ .when( ForkedBooter.class, "lookupDecoderFactory", anyString() );
+
+ try ( final ServerSocketChannel server = ServerSocketChannel.open() )
+ {
+ if ( server.supportedOptions().contains( SO_REUSEADDR ) )
+ {
+ server.setOption( SO_REUSEADDR, true );
+ }
+
+ if ( server.supportedOptions().contains( TCP_NODELAY ) )
+ {
+ server.setOption( TCP_NODELAY, true );
+ }
+
+ if ( server.supportedOptions().contains( SO_KEEPALIVE ) )
+ {
+ server.setOption( SO_KEEPALIVE, true );
+ }
+
+ server.bind( new InetSocketAddress( 0 ) );
+ int serverPort = ( (InetSocketAddress) server.getLocalAddress() ).getPort();
+ final UUID uuid = UUID.randomUUID();
+ String url = "tcp://127.0.0.1:" + serverPort + "?auth=" + uuid;
+ try ( final MasterProcessChannelProcessorFactory factory =
+ invokeMethod( ForkedBooter.class, "lookupDecoderFactory", url ) )
+ {
+ assertThat( factory )
+ .isInstanceOf( SurefireMasterProcessChannelProcessorFactory.class );
+
+ FutureTask<Boolean> task = new FutureTask<>( new Callable<Boolean>()
+ {
+ @Override
+ public Boolean call()
+ {
+ try
+ {
+ SocketChannel channel = server.accept();
+ ByteBuffer bb = ByteBuffer.allocate( UUID_STRING_LENGTH );
+ int read = channel.read( bb );
+ assertThat( read )
+ .isEqualTo( UUID_STRING_LENGTH );
+ bb.flip();
+ assertThat( new String( bb.array(), US_ASCII ) )
+ .isEqualTo( uuid.toString() );
+ return true;
+ }
+ catch ( IOException e )
+ {
+ return false;
+ }
+ }
+ } );
+
+ Thread t = new Thread( task );
+ t.setDaemon( true );
+ t.start();
+
+ factory.connect( url );
+
+ try
+ {
+ task.get( 10, SECONDS );
+ }
+ finally
+ {
+ factory.close();
+ }
+ }
+ }
+ }
+
+ @Test
public void testFlushEventChannelOnExit() throws Exception
{
mockStatic( ShutdownHookUtils.class );
@@ -385,4 +472,26 @@ public class ForkedBooterMockTest
} ).when( ShutdownHookUtils.class, "addShutDownHook", any( Thread.class ) );
invokeMethod( booter, "flushEventChannelOnExit" );
}
+
+ @Test
+ public void shouldParseUUID() throws Exception
+ {
+ SurefireMasterProcessChannelProcessorFactory factory = new SurefireMasterProcessChannelProcessorFactory();
+ UUID uuid = UUID.randomUUID();
+ URI uri = new URI( "tcp://127.0.0.1:12345?auth=" + uuid );
+ UUID parsed = invokeMethod( factory, "parseAuth", uri );
+ assertThat( parsed )
+ .isEqualTo( uuid );
+ }
+
+ @Test
+ public void shouldNotParseUUID() throws Exception
+ {
+ SurefireMasterProcessChannelProcessorFactory factory = new SurefireMasterProcessChannelProcessorFactory();
+ UUID uuid = UUID.randomUUID();
+ URI uri = new URI( "tcp://127.0.0.1:12345?xxx=" + uuid );
+ UUID parsed = invokeMethod( factory, "parseAuth", uri );
+ assertThat( parsed )
+ .isNull();
+ }
}
diff --git a/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/ForkNodeArguments.java b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/ForkNodeArguments.java
index bdd0b2e..9397c38 100644
--- a/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/ForkNodeArguments.java
+++ b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/ForkNodeArguments.java
@@ -24,6 +24,7 @@ import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
import javax.annotation.Nonnegative;
import javax.annotation.Nonnull;
import java.io.File;
+import java.util.UUID;
/**
* The properties related to the current JVM.
@@ -33,6 +34,9 @@ import java.io.File;
*/
public interface ForkNodeArguments
{
+ @Nonnull
+ UUID getSessionId();
+
/**
* The index of the forked JVM, from 1 to N.
*