[MNG-7511] Ensure the degreeOfConcurrency is a positive number in MavenExecutionRequest

This closes #767
This commit is contained in:
Josef Cacek 2022-07-12 20:15:25 +02:00 committed by Michael Osipov
parent f05397a88d
commit 03b1faff7e
6 changed files with 93 additions and 27 deletions

View File

@ -134,7 +134,7 @@ public class LifecycleStarter
} }
int degreeOfConcurrency = session.getRequest().getDegreeOfConcurrency(); int degreeOfConcurrency = session.getRequest().getDegreeOfConcurrency();
if ( degreeOfConcurrency >= 2 ) if ( degreeOfConcurrency > 1 )
{ {
logger.info( "" ); logger.info( "" );
logger.info( String.format( "Using the %s implementation with a thread count of %d", logger.info( String.format( "Using the %s implementation with a thread count of %d",

View File

@ -84,7 +84,7 @@ public class MultiThreadedBuilder
throws ExecutionException, InterruptedException throws ExecutionException, InterruptedException
{ {
int nThreads = Math.min( session.getRequest().getDegreeOfConcurrency(), session.getProjects().size() ); int nThreads = Math.min( session.getRequest().getDegreeOfConcurrency(), session.getProjects().size() );
boolean parallel = nThreads >= 2; boolean parallel = nThreads > 1;
// Propagate the parallel flag to the root session and all of the cloned sessions in each project segment // Propagate the parallel flag to the root session and all of the cloned sessions in each project segment
session.setParallel( parallel ); session.setParallel( parallel );
for ( ProjectSegment segment : projectBuilds ) for ( ProjectSegment segment : projectBuilds )

View File

@ -20,6 +20,7 @@ package org.apache.maven.execution;
*/ */
import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertNotSame;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;

View File

@ -153,7 +153,7 @@ public class CLIManager
options.addOption( Option.builder( Character.toString( SHOW_VERSION ) ).longOpt( "show-version" ).desc( "Display version information WITHOUT stopping build" ).build() ); options.addOption( Option.builder( Character.toString( SHOW_VERSION ) ).longOpt( "show-version" ).desc( "Display version information WITHOUT stopping build" ).build() );
options.addOption( Option.builder( ENCRYPT_MASTER_PASSWORD ).longOpt( "encrypt-master-password" ).hasArg().optionalArg( true ).desc( "Encrypt master security password" ).build() ); options.addOption( Option.builder( ENCRYPT_MASTER_PASSWORD ).longOpt( "encrypt-master-password" ).hasArg().optionalArg( true ).desc( "Encrypt master security password" ).build() );
options.addOption( Option.builder( ENCRYPT_PASSWORD ).longOpt( "encrypt-password" ).hasArg().optionalArg( true ).desc( "Encrypt server password" ).build() ); options.addOption( Option.builder( ENCRYPT_PASSWORD ).longOpt( "encrypt-password" ).hasArg().optionalArg( true ).desc( "Encrypt server password" ).build() );
options.addOption( Option.builder( THREADS ).longOpt( "threads" ).hasArg().desc( "Thread count, for instance 2.0C where C is core multiplied" ).build() ); options.addOption( Option.builder( THREADS ).longOpt( "threads" ).hasArg().desc( "Thread count, for instance 4 (int) or 2C/2.5C (int/float) where C is core multiplied" ).build() );
options.addOption( Option.builder( LEGACY_LOCAL_REPOSITORY ).longOpt( "legacy-local-repository" ).desc( "Use Maven 2 Legacy Local Repository behaviour, ie no use of _remote.repositories. Can also be activated by using -Dmaven.legacyLocalRepo=true" ).build() ); options.addOption( Option.builder( LEGACY_LOCAL_REPOSITORY ).longOpt( "legacy-local-repository" ).desc( "Use Maven 2 Legacy Local Repository behaviour, ie no use of _remote.repositories. Can also be activated by using -Dmaven.legacyLocalRepo=true" ).build() );
options.addOption( Option.builder( BUILDER ).longOpt( "builder" ).hasArg().desc( "The id of the build strategy to use" ).build() ); options.addOption( Option.builder( BUILDER ).longOpt( "builder" ).hasArg().desc( "The id of the build strategy to use" ).build() );
options.addOption( Option.builder( NO_TRANSFER_PROGRESS ).longOpt( "no-transfer-progress" ).desc( "Do not display transfer progress when downloading or uploading" ).build() ); options.addOption( Option.builder( NO_TRANSFER_PROGRESS ).longOpt( "no-transfer-progress" ).desc( "Do not display transfer progress when downloading or uploading" ).build() );

View File

@ -24,6 +24,7 @@ import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Option; import org.apache.commons.cli.Option;
import org.apache.commons.cli.ParseException; import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.UnrecognizedOptionException; import org.apache.commons.cli.UnrecognizedOptionException;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.maven.BuildAbort; import org.apache.maven.BuildAbort;
import org.apache.maven.InternalErrorException; import org.apache.maven.InternalErrorException;
import org.apache.maven.Maven; import org.apache.maven.Maven;
@ -1421,18 +1422,11 @@ public class MavenCli
if ( threadConfiguration != null ) if ( threadConfiguration != null )
{ {
// int degreeOfConcurrency = calculateDegreeOfConcurrency( threadConfiguration );
// Default to the standard multithreaded builder if ( degreeOfConcurrency > 1 )
//
request.setBuilderId( "multithreaded" );
if ( threadConfiguration.contains( "C" ) )
{ {
request.setDegreeOfConcurrency( calculateDegreeOfConcurrencyWithCoreMultiplier( threadConfiguration ) ); request.setBuilderId( "multithreaded" );
} request.setDegreeOfConcurrency( degreeOfConcurrency );
else
{
request.setDegreeOfConcurrency( Integer.parseInt( threadConfiguration ) );
} }
} }
@ -1698,10 +1692,56 @@ public class MavenCli
} }
} }
int calculateDegreeOfConcurrencyWithCoreMultiplier( String threadConfiguration ) int calculateDegreeOfConcurrency( String threadConfiguration )
{ {
int procs = Runtime.getRuntime().availableProcessors(); if ( threadConfiguration.endsWith( "C" ) )
return (int) ( Float.parseFloat( threadConfiguration.replace( "C", "" ) ) * procs ); {
threadConfiguration = threadConfiguration.substring( 0, threadConfiguration.length() - 1 );
if ( !NumberUtils.isParsable( threadConfiguration ) )
{
throw new IllegalArgumentException( "Invalid threads core multiplier value: '" + threadConfiguration
+ "C'. Supported are int and float values ending with C." );
}
float coreMultiplier = Float.parseFloat( threadConfiguration );
if ( coreMultiplier <= 0.0f )
{
throw new IllegalArgumentException( "Invalid threads core multiplier value: '" + threadConfiguration
+ "C'. Value must be positive." );
}
int procs = Runtime.getRuntime().availableProcessors();
int threads = (int) ( coreMultiplier * procs );
return threads == 0 ? 1 : threads;
}
else
{
if ( !NumberUtils.isParsable( threadConfiguration ) )
{
throw new IllegalArgumentException( "Invalid threads value: '" + threadConfiguration
+ "'. Supported are int values." );
}
try
{
int threads = Integer.parseInt( threadConfiguration );
if ( threads <= 0 )
{
throw new IllegalArgumentException( "Invalid threads value: '" + threadConfiguration
+ "'. Value must be positive." );
}
return threads;
}
catch ( NumberFormatException e )
{
throw new IllegalArgumentException( "Invalid threads value: '" + threadConfiguration
+ "'. Supported are integer values." );
}
}
} }
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------

View File

@ -146,18 +146,43 @@ public class MavenCliTest
} }
@Test @Test
public void testCalculateDegreeOfConcurrencyWithCoreMultiplier() public void testCalculateDegreeOfConcurrency()
{ {
int cores = Runtime.getRuntime().availableProcessors(); assertThrows( IllegalArgumentException.class,
// -T2.2C () -> cli.calculateDegreeOfConcurrency( "0" ) );
assertEquals( (int) ( cores * 2.2 ), cli.calculateDegreeOfConcurrencyWithCoreMultiplier( "C2.2" ) ); assertThrows( IllegalArgumentException.class,
// -TC2.2 () -> cli.calculateDegreeOfConcurrency( "-1" ) );
assertEquals( (int) ( cores * 2.2 ), cli.calculateDegreeOfConcurrencyWithCoreMultiplier( "2.2C" ) ); assertThrows( IllegalArgumentException.class,
() -> cli.calculateDegreeOfConcurrency( "0x4" ) );
assertThrows( IllegalArgumentException.class,
() -> cli.calculateDegreeOfConcurrency( "1.0" ) );
assertThrows( IllegalArgumentException.class,
() -> cli.calculateDegreeOfConcurrency( "1." ) );
assertThrows( IllegalArgumentException.class,
() -> cli.calculateDegreeOfConcurrency( "AA" ) );
assertThrows( IllegalArgumentException.class,
() -> cli.calculateDegreeOfConcurrency( "C" ) );
assertThrows( IllegalArgumentException.class,
() -> cli.calculateDegreeOfConcurrency( "C2.2C" ) );
assertThrows( IllegalArgumentException.class,
() -> cli.calculateDegreeOfConcurrency( "C2.2" ) );
assertThrows( IllegalArgumentException.class,
() -> cli.calculateDegreeOfConcurrency( "2C2" ) );
assertThrows( IllegalArgumentException.class,
() -> cli.calculateDegreeOfConcurrency( "CXXX" ) );
assertThrows( IllegalArgumentException.class,
() -> cli.calculateDegreeOfConcurrency( "XXXC" ) );
int cpus = Runtime.getRuntime().availableProcessors();
assertEquals( (int) ( cpus * 2.2 ), cli.calculateDegreeOfConcurrency( "2.2C" ) );
assertEquals( 1, cli.calculateDegreeOfConcurrency( "0.0001C" ) );
assertThrows( IllegalArgumentException.class,
() -> cli.calculateDegreeOfConcurrency( "2.C" ) );
assertThrows( IllegalArgumentException.class,
() -> cli.calculateDegreeOfConcurrency( "-2.2C" ) );
assertThrows( IllegalArgumentException.class,
() -> cli.calculateDegreeOfConcurrency( "0C" ) );
assertThrows(
NumberFormatException.class,
() -> cli.calculateDegreeOfConcurrencyWithCoreMultiplier( "CXXX" ),
"Should have failed with a NumberFormatException" );
} }
@Test @Test