You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by rf...@apache.org on 2020/06/20 09:33:25 UTC

[maven] 12/31: Extract public interface

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

rfscholte pushed a commit to branch MNG-5760
in repository https://gitbox.apache.org/repos/asf/maven.git

commit 504a0efdb9906eae99cdc2b7e21bb1a7c24cbb8c
Author: Maarten Mulders <ma...@infosupport.com>
AuthorDate: Sat May 23 19:56:34 2020 +0200

    Extract public interface
---
 .../maven/execution/BuildResumptionManager.java    | 299 ++-------------------
 ...ger.java => DefaultBuildResumptionManager.java} |  29 +-
 ...java => DefaultBuildResumptionManagerTest.java} |   4 +-
 3 files changed, 36 insertions(+), 296 deletions(-)

diff --git a/maven-core/src/main/java/org/apache/maven/execution/BuildResumptionManager.java b/maven-core/src/main/java/org/apache/maven/execution/BuildResumptionManager.java
index e0ab008..f3d41aa 100644
--- a/maven-core/src/main/java/org/apache/maven/execution/BuildResumptionManager.java
+++ b/maven-core/src/main/java/org/apache/maven/execution/BuildResumptionManager.java
@@ -19,79 +19,40 @@ package org.apache.maven.execution;
  * under the License.
  */
 
-import com.google.common.annotations.VisibleForTesting;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.maven.lifecycle.LifecycleExecutionException;
-import org.apache.maven.model.Dependency;
 import org.apache.maven.project.MavenProject;
-import org.codehaus.plexus.logging.Logger;
 
-import javax.inject.Inject;
-import javax.inject.Named;
-import javax.inject.Singleton;
-import java.io.IOException;
-import java.io.Reader;
-import java.io.Writer;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.Arrays;
 import java.util.List;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Properties;
-import java.util.stream.Collectors;
-
-import static java.util.Comparator.comparing;
 
 /**
- * This class contains most of the logic needed for the --resume / -r feature.
- * It persists information in a properties file to ensure newer builds, using the -r feature,
- *   skip successfully built projects.
+ * This class describes most of the logic needed for the --resume / -r feature. Its goal is to ensure newer
+ * builds of the same project that have the -r command-line flag skip successfully built projects during earlier
+ * invocations of Maven.
  */
-@Named
-@Singleton
-public class BuildResumptionManager
+public interface BuildResumptionManager
 {
-    private static final String RESUME_PROPERTIES_FILENAME = "resume.properties";
-    private static final String RESUME_FROM_PROPERTY = "resumeFrom";
-    private static final String EXCLUDED_PROJECTS_PROPERTY = "excludedProjects";
-    private static final String PROPERTY_DELIMITER = ", ";
-
-    @Inject
-    private Logger logger;
-    
-    public boolean persistResumptionData( MavenExecutionResult result, MavenProject rootProject )
-    {
-        Properties properties = determineResumptionProperties( result );
-
-        if ( properties.isEmpty() )
-        {
-            logger.debug( "Will not create " + RESUME_PROPERTIES_FILENAME + " file: nothing to resume from" );
-            return false;
-        }
-
-        return writeResumptionFile( rootProject, properties );
-    }
+    /**
+     * Persists any data needed to resume the build at a later point in time, using a new Maven invocation. This method
+     * may also decide it is not needed or meaningful to persist such data, and return <code>false</code> to indicate
+     * so.
+     *
+     * @param result The result of the current Maven invocation.
+     * @param rootProject The root project that is being built.
+     * @return Whether any data was persisted.
+     */
+    boolean persistResumptionData( final MavenExecutionResult result, final MavenProject rootProject );
 
-    public void applyResumptionData( MavenExecutionRequest request, MavenProject rootProject )
-    {
-        Properties properties = loadResumptionFile( rootProject.getBuild().getDirectory() );
-        applyResumptionProperties( request, properties );
-    }
+    /**
+     * Uses previously stored resumption data to enrich an existing execution request.
+     * @param request The execution request that will be enriched.
+     * @param rootProject The root project that is being built.
+     */
+    void applyResumptionData( final MavenExecutionRequest request, final MavenProject rootProject );
 
-    public void removeResumptionData( MavenProject rootProject )
-    {
-        Path resumeProperties = Paths.get( rootProject.getBuild().getDirectory(), RESUME_PROPERTIES_FILENAME );
-        try
-        {
-            Files.deleteIfExists( resumeProperties );
-        }
-        catch ( IOException e )
-        {
-            logger.warn( "Could not delete " + RESUME_PROPERTIES_FILENAME + " file. ", e );
-        }
-    }
+    /**
+     * Removes previously stored resumption data.
+     * @param rootProject The root project that is being built.
+     */
+    void removeResumptionData( final MavenProject rootProject );
 
     /**
      * A helper method to determine the value to resume the build with {@code -rf} taking into account the edge case
@@ -109,213 +70,5 @@ public class BuildResumptionManager
      * @return Value for -rf flag to resume build exactly from place where it failed ({@code :artifactId} in general
      * and {@code groupId:artifactId} when there is a name clash).
      */
-    public String getResumeFromSelector( List<MavenProject> mavenProjects, MavenProject failedProject )
-    {
-        boolean hasOverlappingArtifactId = mavenProjects.stream()
-                .filter( project -> failedProject.getArtifactId().equals( project.getArtifactId() ) )
-                .count() > 1;
-
-        if ( hasOverlappingArtifactId )
-        {
-            return failedProject.getGroupId() + ":" + failedProject.getArtifactId();
-        }
-
-        return ":" + failedProject.getArtifactId();
-    }
-
-    @VisibleForTesting
-    Properties determineResumptionProperties( MavenExecutionResult result )
-    {
-        Properties properties = new Properties();
-
-        List<MavenProject> failedProjects = getFailedProjectsInOrder( result );
-        if ( !failedProjects.isEmpty() )
-        {
-            MavenProject resumeFromProject = failedProjects.get( 0 );
-            Optional<String> resumeFrom = getResumeFrom( result, resumeFromProject );
-            Optional<String> projectsToSkip = determineProjectsToSkip( result, failedProjects, resumeFromProject );
-
-            resumeFrom.ifPresent( value -> properties.setProperty( RESUME_FROM_PROPERTY, value ) );
-            projectsToSkip.ifPresent( value -> properties.setProperty( EXCLUDED_PROJECTS_PROPERTY, value ) );
-        }
-        else
-        {
-            logger.warn( "Could not create " + RESUME_PROPERTIES_FILENAME + " file: no failed projects found" );
-        }
-
-        return properties;
-    }
-
-    private List<MavenProject> getFailedProjectsInOrder( MavenExecutionResult result )
-    {
-        List<MavenProject> sortedProjects = result.getTopologicallySortedProjects();
-
-        return result.getExceptions().stream()
-                .filter( LifecycleExecutionException.class::isInstance )
-                .map( LifecycleExecutionException.class::cast )
-                .map( LifecycleExecutionException::getProject )
-                .sorted( comparing( sortedProjects::indexOf ) )
-                .collect( Collectors.toList() );
-    }
-
-    /**
-     * Determine the project where the next build can be resumed from.
-     * If the failed project is the first project of the build,
-     * it does not make sense to use --resume-from, so the result will be empty.
-     * @param result The result of the Maven build.
-     * @param failedProject The first failed project of the build.
-     * @return An optional containing the resume-from suggestion.
-     */
-    private Optional<String> getResumeFrom( MavenExecutionResult result, MavenProject failedProject )
-    {
-        List<MavenProject> allSortedProjects = result.getTopologicallySortedProjects();
-        if ( !allSortedProjects.get( 0 ).equals( failedProject ) )
-        {
-            return Optional.of( String.format( "%s:%s", failedProject.getGroupId(), failedProject.getArtifactId() ) );
-        }
-
-        return Optional.empty();
-    }
-
-    /**
-     * Projects after the first failed project could have succeeded by using -T or --fail-at-end.
-     * These projects can be skipped from later builds.
-     * This is not the case these projects are dependent on one of the failed projects.
-     * @param result The result of the Maven build.
-     * @param failedProjects The list of failed projects in the build.
-     * @param resumeFromProject The project where the build will be resumed with in the next run.
-     * @return An optional containing a comma separated list of projects which can be skipped,
-     *   or an empty optional if no projects can be skipped.
-     */
-    private Optional<String> determineProjectsToSkip( MavenExecutionResult result, List<MavenProject> failedProjects,
-                                                      MavenProject resumeFromProject )
-    {
-        List<MavenProject> allProjects = result.getTopologicallySortedProjects();
-        int resumeFromProjectIndex = allProjects.indexOf( resumeFromProject );
-        List<MavenProject> remainingProjects = allProjects.subList( resumeFromProjectIndex + 1, allProjects.size() );
-
-        List<GroupArtifactPair> failedProjectsGAList = failedProjects.stream()
-                .map( GroupArtifactPair::new )
-                .collect( Collectors.toList() );
-
-        String projectsToSkip = remainingProjects.stream()
-                .filter( project -> result.getBuildSummary( project ) instanceof BuildSuccess )
-                .filter( project -> hasNoDependencyOnProjects( project, failedProjectsGAList ) )
-                .map( project -> String.format( "%s:%s", project.getGroupId(), project.getArtifactId() ) )
-                .collect( Collectors.joining( PROPERTY_DELIMITER ) );
-
-        if ( !StringUtils.isEmpty( projectsToSkip ) )
-        {
-            return Optional.of( projectsToSkip );
-        }
-
-        return Optional.empty();
-    }
-
-    private boolean hasNoDependencyOnProjects( MavenProject project, List<GroupArtifactPair> projectsGAs )
-    {
-        return project.getDependencies().stream()
-                .map( GroupArtifactPair::new )
-                .noneMatch( projectsGAs::contains );
-    }
-
-    private boolean writeResumptionFile( MavenProject rootProject, Properties properties )
-    {
-        Path resumeProperties = Paths.get( rootProject.getBuild().getDirectory(), RESUME_PROPERTIES_FILENAME );
-        try
-        {
-            Files.createDirectories( resumeProperties.getParent() );
-            try ( Writer writer = Files.newBufferedWriter( resumeProperties ) )
-            {
-                properties.store( writer, null );
-            }
-        }
-        catch ( IOException e )
-        {
-            logger.warn( "Could not create " + RESUME_PROPERTIES_FILENAME + " file. ", e );
-            return false;
-        }
-
-        return true;
-    }
-
-    private Properties loadResumptionFile( String rootBuildDirectory )
-    {
-        Properties properties = new Properties();
-        Path path = Paths.get( rootBuildDirectory, RESUME_PROPERTIES_FILENAME );
-        if ( !Files.exists( path ) )
-        {
-            logger.warn( "The " + path + " file does not exist. The --resume / -r feature will not work." );
-            return properties;
-        }
-
-        try ( Reader reader = Files.newBufferedReader( path ) )
-        {
-            properties.load( reader );
-        }
-        catch ( IOException e )
-        {
-            logger.warn( "Unable to read " + path + ". The --resume / -r feature will not work." );
-        }
-
-        return properties;
-    }
-
-    @VisibleForTesting
-    void applyResumptionProperties( MavenExecutionRequest request, Properties properties )
-    {
-        if ( properties.containsKey( RESUME_FROM_PROPERTY ) && StringUtils.isEmpty( request.getResumeFrom() ) )
-        {
-            String propertyValue = properties.getProperty( RESUME_FROM_PROPERTY );
-            request.setResumeFrom( propertyValue );
-            logger.info( "Resuming from " + propertyValue + " due to the --resume / -r feature." );
-        }
-
-        if ( properties.containsKey( EXCLUDED_PROJECTS_PROPERTY ) )
-        {
-            String propertyValue = properties.getProperty( EXCLUDED_PROJECTS_PROPERTY );
-            String[] excludedProjects = propertyValue.split( PROPERTY_DELIMITER );
-            request.getExcludedProjects().addAll( Arrays.asList( excludedProjects ) );
-            logger.info( "Additionally excluding projects '" + propertyValue + "' due to the --resume / -r feature." );
-        }
-    }
-
-    private static class GroupArtifactPair
-    {
-        private final String groupId;
-        private final String artifactId;
-
-        GroupArtifactPair( MavenProject project )
-        {
-            this.groupId = project.getGroupId();
-            this.artifactId = project.getArtifactId();
-        }
-
-        GroupArtifactPair( Dependency dependency )
-        {
-            this.groupId = dependency.getGroupId();
-            this.artifactId = dependency.getArtifactId();
-        }
-
-        @Override
-        public boolean equals( Object o )
-        {
-            if ( this == o )
-            {
-                return true;
-            }
-            if ( o == null || getClass() != o.getClass() )
-            {
-                return false;
-            }
-            GroupArtifactPair that = (GroupArtifactPair) o;
-            return Objects.equals( groupId, that.groupId ) && Objects.equals( artifactId, that.artifactId );
-        }
-
-        @Override
-        public int hashCode()
-        {
-            return Objects.hash( groupId, artifactId );
-        }
-    }
+    String getResumeFromSelector( final List<MavenProject> mavenProjects, final MavenProject failedProject );
 }
diff --git a/maven-core/src/main/java/org/apache/maven/execution/BuildResumptionManager.java b/maven-core/src/main/java/org/apache/maven/execution/DefaultBuildResumptionManager.java
similarity index 89%
copy from maven-core/src/main/java/org/apache/maven/execution/BuildResumptionManager.java
copy to maven-core/src/main/java/org/apache/maven/execution/DefaultBuildResumptionManager.java
index e0ab008..d08478f 100644
--- a/maven-core/src/main/java/org/apache/maven/execution/BuildResumptionManager.java
+++ b/maven-core/src/main/java/org/apache/maven/execution/DefaultBuildResumptionManager.java
@@ -45,13 +45,12 @@ import java.util.stream.Collectors;
 import static java.util.Comparator.comparing;
 
 /**
- * This class contains most of the logic needed for the --resume / -r feature.
- * It persists information in a properties file to ensure newer builds, using the -r feature,
- *   skip successfully built projects.
+ * This implementation of {@link BuildResumptionManager} persists information in a properties file. The file is stored
+ * in the build output directory under the Maven execution root.
  */
 @Named
 @Singleton
-public class BuildResumptionManager
+public class DefaultBuildResumptionManager implements BuildResumptionManager
 {
     private static final String RESUME_PROPERTIES_FILENAME = "resume.properties";
     private static final String RESUME_FROM_PROPERTY = "resumeFrom";
@@ -60,7 +59,8 @@ public class BuildResumptionManager
 
     @Inject
     private Logger logger;
-    
+
+    @Override
     public boolean persistResumptionData( MavenExecutionResult result, MavenProject rootProject )
     {
         Properties properties = determineResumptionProperties( result );
@@ -74,12 +74,14 @@ public class BuildResumptionManager
         return writeResumptionFile( rootProject, properties );
     }
 
+    @Override
     public void applyResumptionData( MavenExecutionRequest request, MavenProject rootProject )
     {
         Properties properties = loadResumptionFile( rootProject.getBuild().getDirectory() );
         applyResumptionProperties( request, properties );
     }
 
+    @Override
     public void removeResumptionData( MavenProject rootProject )
     {
         Path resumeProperties = Paths.get( rootProject.getBuild().getDirectory(), RESUME_PROPERTIES_FILENAME );
@@ -93,22 +95,7 @@ public class BuildResumptionManager
         }
     }
 
-    /**
-     * A helper method to determine the value to resume the build with {@code -rf} taking into account the edge case
-     *   where multiple modules in the reactor have the same artifactId.
-     * <p>
-     * {@code -rf :artifactId} will pick up the first module which matches, but when multiple modules in the reactor
-     *   have the same artifactId, effective failed module might be later in build reactor.
-     * This means that developer will either have to type groupId or wait for build execution of all modules which
-     *   were fine, but they are still before one which reported errors.
-     * <p>Then the returned value is {@code groupId:artifactId} when there is a name clash and
-     * {@code :artifactId} if there is no conflict.
-     *
-     * @param mavenProjects Maven projects which are part of build execution.
-     * @param failedProject Project which has failed.
-     * @return Value for -rf flag to resume build exactly from place where it failed ({@code :artifactId} in general
-     * and {@code groupId:artifactId} when there is a name clash).
-     */
+    @Override
     public String getResumeFromSelector( List<MavenProject> mavenProjects, MavenProject failedProject )
     {
         boolean hasOverlappingArtifactId = mavenProjects.stream()
diff --git a/maven-core/src/test/java/org/apache/maven/execution/BuildResumptionManagerTest.java b/maven-core/src/test/java/org/apache/maven/execution/DefaultBuildResumptionManagerTest.java
similarity index 98%
rename from maven-core/src/test/java/org/apache/maven/execution/BuildResumptionManagerTest.java
rename to maven-core/src/test/java/org/apache/maven/execution/DefaultBuildResumptionManagerTest.java
index 5c3129b..a0c913b 100644
--- a/maven-core/src/test/java/org/apache/maven/execution/BuildResumptionManagerTest.java
+++ b/maven-core/src/test/java/org/apache/maven/execution/DefaultBuildResumptionManagerTest.java
@@ -40,13 +40,13 @@ import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.*;
 
 @RunWith( MockitoJUnitRunner.class )
-public class BuildResumptionManagerTest
+public class DefaultBuildResumptionManagerTest
 {
     @Mock
     private Logger logger;
 
     @InjectMocks
-    private BuildResumptionManager buildResumptionManager;
+    private DefaultBuildResumptionManager buildResumptionManager;
 
     private MavenExecutionResult result;