You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by gn...@apache.org on 2022/11/19 15:56:31 UTC

[maven-mvnd] branch master updated: Switch to maven 4.0.0-alpha-2 (#718)

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

gnodet pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven-mvnd.git


The following commit(s) were added to refs/heads/master by this push:
     new 895381b  Switch to maven 4.0.0-alpha-2 (#718)
895381b is described below

commit 895381b43fbfa11d753af99c2b07f7d525083a2d
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Sat Nov 19 16:56:26 2022 +0100

    Switch to maven 4.0.0-alpha-2 (#718)
---
 .../java/org/apache/maven/cli/DaemonMavenCli.java  | 477 +++++++++++----------
 .../maven/project/CachingProjectBuilder.java       | 192 ++++-----
 .../apache/maven/project/SnapshotModelCache.java   |   4 +-
 .../mvnd/execution/BuildResumptionAnalyzer.java    |  52 ---
 .../mvnd/execution/BuildResumptionData.java        |  60 ---
 .../execution/BuildResumptionDataRepository.java   |  72 ----
 .../BuildResumptionPersistenceException.java       |  46 --
 .../execution/DefaultBuildResumptionAnalyzer.java  |  85 ----
 .../DefaultBuildResumptionDataRepository.java      | 150 -------
 .../mvnd/plugin/CachingPluginVersionResolver.java  |  11 +
 .../mvnd/plugin/CliMavenPluginManager.java         |  36 +-
 .../main/resources/META-INF/maven/extension.xml    |   5 +
 dist/src/main/provisio/maven-distro.xml            |   8 +-
 .../org/mvndaemon/mvnd/it/InteractiveTest.java     |   2 +
 .../mvnd/it/NewManagedModuleNativeIT.java          |   4 +
 pom.xml                                            |  11 +-
 16 files changed, 381 insertions(+), 834 deletions(-)

diff --git a/daemon/src/main/java/org/apache/maven/cli/DaemonMavenCli.java b/daemon/src/main/java/org/apache/maven/cli/DaemonMavenCli.java
index 5d23c9a..e088316 100644
--- a/daemon/src/main/java/org/apache/maven/cli/DaemonMavenCli.java
+++ b/daemon/src/main/java/org/apache/maven/cli/DaemonMavenCli.java
@@ -24,8 +24,6 @@ import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.PrintStream;
 import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -37,7 +35,7 @@ import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Properties;
 import java.util.Set;
-import java.util.StringTokenizer;
+import java.util.function.Consumer;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
@@ -45,6 +43,7 @@ import java.util.stream.Stream;
 import org.apache.commons.cli.CommandLine;
 import org.apache.commons.cli.Option;
 import org.apache.commons.cli.ParseException;
+import org.apache.commons.lang3.math.NumberUtils;
 import org.apache.maven.InternalErrorException;
 import org.apache.maven.Maven;
 import org.apache.maven.building.FileSource;
@@ -61,10 +60,7 @@ import org.apache.maven.eventspy.internal.EventSpyDispatcher;
 import org.apache.maven.exception.DefaultExceptionHandler;
 import org.apache.maven.exception.ExceptionHandler;
 import org.apache.maven.exception.ExceptionSummary;
-import org.apache.maven.execution.MavenExecutionRequest;
-import org.apache.maven.execution.MavenExecutionRequestPopulationException;
-import org.apache.maven.execution.MavenExecutionRequestPopulator;
-import org.apache.maven.execution.MavenExecutionResult;
+import org.apache.maven.execution.*;
 import org.apache.maven.execution.scope.internal.MojoExecutionScopeModule;
 import org.apache.maven.extension.internal.CoreExports;
 import org.apache.maven.extension.internal.CoreExtensionEntry;
@@ -101,9 +97,6 @@ import org.mvndaemon.mvnd.cache.invalidating.InvalidatingProjectArtifactsCache;
 import org.mvndaemon.mvnd.cli.EnvHelper;
 import org.mvndaemon.mvnd.common.Environment;
 import org.mvndaemon.mvnd.common.Os;
-import org.mvndaemon.mvnd.execution.BuildResumptionPersistenceException;
-import org.mvndaemon.mvnd.execution.DefaultBuildResumptionAnalyzer;
-import org.mvndaemon.mvnd.execution.DefaultBuildResumptionDataRepository;
 import org.mvndaemon.mvnd.logging.internal.Slf4jLoggerManager;
 import org.mvndaemon.mvnd.logging.smart.BuildEventListener;
 import org.mvndaemon.mvnd.logging.smart.LoggingExecutionListener;
@@ -177,7 +170,9 @@ public class DaemonMavenCli {
 
     private final LoggingExecutionListener executionListener;
 
-    /** Non-volatile, assuming that it is accessed only from the main thread */
+    /**
+     * Non-volatile, assuming that it is accessed only from the main thread
+     */
     private BuildEventListener buildEventListener = BuildEventListener.dummy();
 
     public DaemonMavenCli() throws Exception {
@@ -354,12 +349,12 @@ public class DaemonMavenCli {
      */
     void logging(CliRequest cliRequest) {
         // LOG LEVEL
-        cliRequest.debug = cliRequest.commandLine.hasOption(CLIManager.DEBUG);
-        cliRequest.quiet = !cliRequest.debug && cliRequest.commandLine.hasOption(CLIManager.QUIET);
-        cliRequest.showErrors = cliRequest.debug || cliRequest.commandLine.hasOption(CLIManager.ERRORS);
+        cliRequest.verbose = cliRequest.commandLine.hasOption(CLIManager.VERBOSE);
+        cliRequest.quiet = !cliRequest.verbose && cliRequest.commandLine.hasOption(CLIManager.QUIET);
+        cliRequest.showErrors = cliRequest.verbose || cliRequest.commandLine.hasOption(CLIManager.ERRORS);
 
         ch.qos.logback.classic.Level level;
-        if (cliRequest.debug) {
+        if (cliRequest.verbose) {
             level = ch.qos.logback.classic.Level.DEBUG;
         } else if (cliRequest.quiet) {
             level = ch.qos.logback.classic.Level.WARN;
@@ -413,7 +408,7 @@ public class DaemonMavenCli {
     }
 
     private void version(CliRequest cliRequest) throws ExitException {
-        if (cliRequest.debug || cliRequest.commandLine.hasOption(CLIManager.VERSION)) {
+        if (cliRequest.verbose || cliRequest.commandLine.hasOption(CLIManager.VERSION)) {
             buildEventListener.log(CLIReportingUtils.showVersion());
             if (cliRequest.commandLine.hasOption(CLIManager.VERSION)) {
                 throw new ExitException(0);
@@ -576,7 +571,7 @@ public class DaemonMavenCli {
                 properties(cliRequest);
                 configure(cliRequest, eventSpyDispatcher, configurationProcessors);
                 LoggingExecutionListener executionListener = container.lookup(LoggingExecutionListener.class);
-                populateRequest(cliRequest, cliRequest.request, slf4jLogger, eventSpyDispatcher,
+                populateRequest(cliRequest, cliRequest.request, eventSpyDispatcher,
                         container.lookup(ModelProcessor.class), createTransferListener(cliRequest), buildEventListener,
                         executionListener);
                 executionRequestPopulator.populateDefaults(cliRequest.request);
@@ -729,18 +724,7 @@ public class DaemonMavenCli {
                 }
             }
 
-            boolean canResume = new DefaultBuildResumptionAnalyzer().determineBuildResumptionData(result).map(resumption -> {
-                try {
-                    Path directory = Paths.get(request.getBaseDirectory()).resolve("target");
-                    new DefaultBuildResumptionDataRepository().persistResumptionData(directory, resumption);
-                    return true;
-                } catch (BuildResumptionPersistenceException e) {
-                    slf4jLogger.warn("Could not persist build resumption data", e);
-                }
-                return false;
-            }).orElse(false);
-
-            if (canResume) {
+            if (result.canResume()) {
                 logBuildResumeHint("mvn <args> -r");
             } else if (!failedProjects.isEmpty()) {
                 List<MavenProject> sortedProjects = result.getTopologicallySortedProjects();
@@ -763,8 +747,6 @@ public class DaemonMavenCli {
                 return 1;
             }
         } else {
-            Path directory = Paths.get(request.getBaseDirectory()).resolve("target");
-            new DefaultBuildResumptionDataRepository().removeResumptionData(directory);
             return 0;
         }
     }
@@ -999,14 +981,13 @@ public class DaemonMavenCli {
     }
 
     private void populateRequest(CliRequest cliRequest) {
-        populateRequest(cliRequest, cliRequest.request, slf4jLogger, eventSpyDispatcher, modelProcessor,
+        populateRequest(cliRequest, cliRequest.request, eventSpyDispatcher, modelProcessor,
                 createTransferListener(cliRequest), buildEventListener, executionListener);
     }
 
-    private static void populateRequest(
+    private void populateRequest(
             CliRequest cliRequest,
             MavenExecutionRequest request,
-            Logger slf4jLogger,
             EventSpyDispatcher eventSpyDispatcher,
             ModelProcessor modelProcessor,
             TransferListener transferListener,
@@ -1014,244 +995,285 @@ public class DaemonMavenCli {
             LoggingExecutionListener executionListener) {
         CommandLine commandLine = cliRequest.commandLine;
         String workingDirectory = cliRequest.workingDirectory;
-        boolean showErrors = cliRequest.showErrors;
-
-        String[] deprecatedOptions = { "up", "npu", "cpu", "npr" };
-        for (String deprecatedOption : deprecatedOptions) {
-            if (commandLine.hasOption(deprecatedOption)) {
-                slf4jLogger.warn("Command line option -{} is deprecated and will be removed in future Maven versions.",
-                        deprecatedOption);
-            }
-        }
-
-        // ----------------------------------------------------------------------
-        // Now that we have everything that we need we will fire up plexus and
-        // bring the maven component to life for use.
-        // ----------------------------------------------------------------------
-
-        if (commandLine.hasOption(CLIManager.BATCH_MODE)) {
-            request.setInteractiveMode(false);
-        }
-
-        boolean noSnapshotUpdates = false;
-        if (commandLine.hasOption(CLIManager.SUPRESS_SNAPSHOT_UPDATES)) {
-            noSnapshotUpdates = true;
-        }
-
-        // ----------------------------------------------------------------------
-        //
-        // ----------------------------------------------------------------------
-
-        List<String> goals = commandLine.getArgList();
-
-        boolean recursive = true;
+        boolean quiet = cliRequest.quiet;
+        boolean verbose = cliRequest.verbose;
+        request.setShowErrors(cliRequest.showErrors); // default: false
+        File baseDirectory = new File(workingDirectory, "").getAbsoluteFile();
 
-        // this is the default behavior.
-        String reactorFailureBehaviour = MavenExecutionRequest.REACTOR_FAIL_FAST;
+        disableOnPresentOption(commandLine, CLIManager.BATCH_MODE, request::setInteractiveMode);
+        enableOnPresentOption(commandLine, CLIManager.SUPPRESS_SNAPSHOT_UPDATES, request::setNoSnapshotUpdates);
+        request.setGoals(commandLine.getArgList());
+        request.setReactorFailureBehavior(determineReactorFailureBehaviour(commandLine));
+        disableOnPresentOption(commandLine, CLIManager.NON_RECURSIVE, request::setRecursive);
+        enableOnPresentOption(commandLine, CLIManager.OFFLINE, request::setOffline);
+        enableOnPresentOption(commandLine, CLIManager.UPDATE_SNAPSHOTS, request::setUpdateSnapshots);
+        request.setGlobalChecksumPolicy(determineGlobalCheckPolicy(commandLine));
+        request.setBaseDirectory(baseDirectory);
+        request.setSystemProperties(cliRequest.systemProperties);
+        request.setUserProperties(cliRequest.userProperties);
+        request.setMultiModuleProjectDirectory(cliRequest.multiModuleProjectDirectory);
+        request.setPom(determinePom(modelProcessor, commandLine, workingDirectory, baseDirectory));
+        request.setTransferListener(transferListener);
+        request.setExecutionListener(executionListener);
 
-        if (commandLine.hasOption(CLIManager.NON_RECURSIVE)) {
-            recursive = false;
-        }
+        ExecutionEventLogger executionEventLogger = new ExecutionEventLogger();
+        executionListener.init(
+                eventSpyDispatcher.chainListener(executionEventLogger),
+                buildEventListener);
 
-        if (commandLine.hasOption(CLIManager.FAIL_FAST)) {
-            reactorFailureBehaviour = MavenExecutionRequest.REACTOR_FAIL_FAST;
-        } else if (commandLine.hasOption(CLIManager.FAIL_AT_END)) {
-            reactorFailureBehaviour = MavenExecutionRequest.REACTOR_FAIL_AT_END;
-        } else if (commandLine.hasOption(CLIManager.FAIL_NEVER)) {
-            reactorFailureBehaviour = MavenExecutionRequest.REACTOR_FAIL_NEVER;
+        if ((request.getPom() != null) && (request.getPom().getParentFile() != null)) {
+            request.setBaseDirectory(request.getPom().getParentFile());
         }
 
-        if (commandLine.hasOption(CLIManager.OFFLINE)) {
-            request.setOffline(true);
-        }
+        request.setResumeFrom(commandLine.getOptionValue(CLIManager.RESUME_FROM));
+        enableOnPresentOption(commandLine, CLIManager.RESUME, request::setResume);
+        request.setMakeBehavior(determineMakeBehavior(commandLine));
+        request.setCacheNotFound(true);
+        request.setCacheTransferError(false);
 
-        boolean updateSnapshots = false;
+        performProjectActivation(commandLine, request.getProjectActivation());
+        performProfileActivation(commandLine, request.getProfileActivation());
 
-        if (commandLine.hasOption(CLIManager.UPDATE_SNAPSHOTS)) {
-            updateSnapshots = true;
+        final String localRepositoryPath = determineLocalRepositoryPath(request);
+        if (localRepositoryPath != null) {
+            request.setLocalRepositoryPath(localRepositoryPath);
         }
 
-        String globalChecksumPolicy = null;
+        //
+        // Builder, concurrency and parallelism
+        //
+        // We preserve the existing methods for builder selection which is to look for various inputs in the threading
+        // configuration. We don't have an easy way to allow a pluggable builder to provide its own configuration
+        // parameters but this is sufficient for now. Ultimately we want components like Builders to provide a way to
+        // extend the command line to accept its own configuration parameters.
+        //
+        final String threadConfiguration = commandLine.getOptionValue(CLIManager.THREADS);
 
-        if (commandLine.hasOption(CLIManager.CHECKSUM_FAILURE_POLICY)) {
-            globalChecksumPolicy = MavenExecutionRequest.CHECKSUM_POLICY_FAIL;
-        } else if (commandLine.hasOption(CLIManager.CHECKSUM_WARNING_POLICY)) {
-            globalChecksumPolicy = MavenExecutionRequest.CHECKSUM_POLICY_WARN;
+        if (threadConfiguration != null) {
+            int degreeOfConcurrency = calculateDegreeOfConcurrency(threadConfiguration);
+            if (degreeOfConcurrency > 1) {
+                request.setBuilderId("multithreaded");
+                request.setDegreeOfConcurrency(degreeOfConcurrency);
+            }
         }
 
-        File baseDirectory = new File(workingDirectory, "").getAbsoluteFile();
-
-        // ----------------------------------------------------------------------
-        // Profile Activation
-        // ----------------------------------------------------------------------
-
-        List<String> activeProfiles = new ArrayList<>();
-
-        List<String> inactiveProfiles = new ArrayList<>();
+        //
+        // Allow the builder to be overridden by the user if requested. The builders are now pluggable.
+        //
+        request.setBuilderId(commandLine.getOptionValue(CLIManager.BUILDER, request.getBuilderId()));
+    }
 
-        if (commandLine.hasOption(CLIManager.ACTIVATE_PROFILES)) {
-            String[] profileOptionValues = commandLine.getOptionValues(CLIManager.ACTIVATE_PROFILES);
-            if (profileOptionValues != null) {
-                for (String profileOptionValue : profileOptionValues) {
-                    StringTokenizer profileTokens = new StringTokenizer(profileOptionValue, ",");
-
-                    while (profileTokens.hasMoreTokens()) {
-                        String profileAction = profileTokens.nextToken().trim();
-
-                        if (profileAction.startsWith("-") || profileAction.startsWith("!")) {
-                            inactiveProfiles.add(profileAction.substring(1));
-                        } else if (profileAction.startsWith("+")) {
-                            activeProfiles.add(profileAction.substring(1));
-                        } else {
-                            activeProfiles.add(profileAction);
-                        }
-                    }
-                }
-            }
+    private String determineLocalRepositoryPath(final MavenExecutionRequest request) {
+        String userDefinedLocalRepo = request.getUserProperties().getProperty(MavenCli.LOCAL_REPO_PROPERTY);
+        if (userDefinedLocalRepo != null) {
+            return userDefinedLocalRepo;
         }
 
-        ExecutionEventLogger executionEventLogger = new ExecutionEventLogger();
-        executionListener.init(
-                eventSpyDispatcher.chainListener(executionEventLogger),
-                buildEventListener);
+        return request.getSystemProperties().getProperty(MavenCli.LOCAL_REPO_PROPERTY);
+    }
 
+    private File determinePom(ModelProcessor modelProcessor, final CommandLine commandLine, final String workingDirectory,
+            final File baseDirectory) {
         String alternatePomFile = null;
         if (commandLine.hasOption(CLIManager.ALTERNATE_POM_FILE)) {
             alternatePomFile = commandLine.getOptionValue(CLIManager.ALTERNATE_POM_FILE);
         }
 
-        request.setBaseDirectory(baseDirectory)
-                .setGoals(goals)
-                .setSystemProperties(cliRequest.systemProperties)
-                .setUserProperties(cliRequest.userProperties)
-                .setReactorFailureBehavior(reactorFailureBehaviour) // default: fail fast
-                .setRecursive(recursive) // default: true
-                .setShowErrors(showErrors) // default: false
-                .addActiveProfiles(activeProfiles) // optional
-                .addInactiveProfiles(inactiveProfiles) // optional
-                .setExecutionListener(executionListener)
-                .setTransferListener(transferListener) // default: batch mode which goes along with interactive
-                .setUpdateSnapshots(updateSnapshots) // default: false
-                .setNoSnapshotUpdates(noSnapshotUpdates) // default: false
-                .setGlobalChecksumPolicy(globalChecksumPolicy) // default: warn
-                .setMultiModuleProjectDirectory(cliRequest.getMultiModuleProjectDirectory());
-
         if (alternatePomFile != null) {
             File pom = resolveFile(new File(alternatePomFile), workingDirectory);
             if (pom.isDirectory()) {
                 pom = new File(pom, "pom.xml");
             }
 
-            request.setPom(pom);
+            return pom;
         } else if (modelProcessor != null) {
             File pom = modelProcessor.locatePom(baseDirectory);
 
             if (pom.isFile()) {
-                request.setPom(pom);
+                return pom;
             }
         }
 
-        if ((request.getPom() != null) && (request.getPom().getParentFile() != null)) {
-            request.setBaseDirectory(request.getPom().getParentFile());
-        }
+        return null;
+    }
 
-        if (commandLine.hasOption(RESUME)) {
-            new DefaultBuildResumptionDataRepository()
-                    .applyResumptionData(request, Paths.get(request.getBaseDirectory()).resolve("target"));
-        }
+    // Visible for testing
+    static void performProjectActivation(final CommandLine commandLine, final ProjectActivation projectActivation) {
+        if (commandLine.hasOption(CLIManager.PROJECT_LIST)) {
+            final String[] optionValues = commandLine.getOptionValues(CLIManager.PROJECT_LIST);
 
-        if (commandLine.hasOption(CLIManager.RESUME_FROM)) {
-            request.setResumeFrom(commandLine.getOptionValue(CLIManager.RESUME_FROM));
-        }
+            if (optionValues == null || optionValues.length == 0) {
+                return;
+            }
 
-        if (commandLine.hasOption(CLIManager.PROJECT_LIST)) {
-            String[] projectOptionValues = commandLine.getOptionValues(CLIManager.PROJECT_LIST);
+            for (final String optionValue : optionValues) {
+                for (String token : optionValue.split(",")) {
+                    String selector = token.trim();
+                    boolean active = true;
+                    if (selector.charAt(0) == '-' || selector.charAt(0) == '!') {
+                        active = false;
+                        selector = selector.substring(1);
+                    } else if (token.charAt(0) == '+') {
+                        selector = selector.substring(1);
+                    }
 
-            List<String> inclProjects = new ArrayList<>();
-            List<String> exclProjects = new ArrayList<>();
+                    boolean optional = selector.charAt(0) == '?';
+                    selector = selector.substring(optional ? 1 : 0);
 
-            if (projectOptionValues != null) {
-                for (String projectOptionValue : projectOptionValues) {
-                    StringTokenizer projectTokens = new StringTokenizer(projectOptionValue, ",");
+                    projectActivation.addProjectActivation(selector, active, optional);
+                }
+            }
+        }
+    }
 
-                    while (projectTokens.hasMoreTokens()) {
-                        String projectAction = projectTokens.nextToken().trim();
+    // Visible for testing
+    static void performProfileActivation(final CommandLine commandLine, final ProfileActivation profileActivation) {
+        if (commandLine.hasOption(CLIManager.ACTIVATE_PROFILES)) {
+            final String[] optionValues = commandLine.getOptionValues(CLIManager.ACTIVATE_PROFILES);
+
+            if (optionValues == null || optionValues.length == 0) {
+                return;
+            }
 
-                        if (projectAction.startsWith("-") || projectAction.startsWith("!")) {
-                            exclProjects.add(projectAction.substring(1));
-                        } else if (projectAction.startsWith("+")) {
-                            inclProjects.add(projectAction.substring(1));
-                        } else {
-                            inclProjects.add(projectAction);
-                        }
+            for (final String optionValue : optionValues) {
+                for (String token : optionValue.split(",")) {
+                    String profileId = token.trim();
+                    boolean active = true;
+                    if (profileId.charAt(0) == '-' || profileId.charAt(0) == '!') {
+                        active = false;
+                        profileId = profileId.substring(1);
+                    } else if (token.charAt(0) == '+') {
+                        profileId = profileId.substring(1);
                     }
+
+                    boolean optional = profileId.charAt(0) == '?';
+                    profileId = profileId.substring(optional ? 1 : 0);
+
+                    profileActivation.addProfileActivation(profileId, active, optional);
                 }
             }
-
-            request.setSelectedProjects(inclProjects);
-            request.setExcludedProjects(exclProjects);
         }
+    }
 
-        if (commandLine.hasOption(CLIManager.ALSO_MAKE) && !commandLine.hasOption(
-                CLIManager.ALSO_MAKE_DEPENDENTS)) {
-            request.setMakeBehavior(MavenExecutionRequest.REACTOR_MAKE_UPSTREAM);
-        } else if (!commandLine.hasOption(CLIManager.ALSO_MAKE) && commandLine.hasOption(
-                CLIManager.ALSO_MAKE_DEPENDENTS)) {
-            request.setMakeBehavior(MavenExecutionRequest.REACTOR_MAKE_DOWNSTREAM);
-        } else if (commandLine.hasOption(CLIManager.ALSO_MAKE) && commandLine.hasOption(
-                CLIManager.ALSO_MAKE_DEPENDENTS)) {
-            request.setMakeBehavior(MavenExecutionRequest.REACTOR_MAKE_BOTH);
+    private ExecutionListener determineExecutionListener(EventSpyDispatcher eventSpyDispatcher) {
+        ExecutionListener executionListener = new ExecutionEventLogger();
+        if (eventSpyDispatcher != null) {
+            return eventSpyDispatcher.chainListener(executionListener);
+        } else {
+            return executionListener;
         }
+    }
 
-        String localRepoProperty = request.getUserProperties().getProperty(MavenCli.LOCAL_REPO_PROPERTY);
-
-        if (localRepoProperty == null) {
-            localRepoProperty = request.getSystemProperties().getProperty(MavenCli.LOCAL_REPO_PROPERTY);
+    private String determineReactorFailureBehaviour(final CommandLine commandLine) {
+        if (commandLine.hasOption(CLIManager.FAIL_FAST)) {
+            return MavenExecutionRequest.REACTOR_FAIL_FAST;
+        } else if (commandLine.hasOption(CLIManager.FAIL_AT_END)) {
+            return MavenExecutionRequest.REACTOR_FAIL_AT_END;
+        } else if (commandLine.hasOption(CLIManager.FAIL_NEVER)) {
+            return MavenExecutionRequest.REACTOR_FAIL_NEVER;
+        } else {
+            // this is the default behavior.
+            return MavenExecutionRequest.REACTOR_FAIL_FAST;
         }
+    }
 
-        if (localRepoProperty != null) {
-            request.setLocalRepositoryPath(localRepoProperty);
+    private String determineMakeBehavior(final CommandLine cl) {
+        if (cl.hasOption(CLIManager.ALSO_MAKE) && !cl.hasOption(CLIManager.ALSO_MAKE_DEPENDENTS)) {
+            return MavenExecutionRequest.REACTOR_MAKE_UPSTREAM;
+        } else if (!cl.hasOption(CLIManager.ALSO_MAKE) && cl.hasOption(CLIManager.ALSO_MAKE_DEPENDENTS)) {
+            return MavenExecutionRequest.REACTOR_MAKE_DOWNSTREAM;
+        } else if (cl.hasOption(CLIManager.ALSO_MAKE) && cl.hasOption(CLIManager.ALSO_MAKE_DEPENDENTS)) {
+            return MavenExecutionRequest.REACTOR_MAKE_BOTH;
+        } else {
+            return null;
         }
+    }
 
-        request.setCacheNotFound(true);
-        request.setCacheTransferError(false);
+    private String determineGlobalCheckPolicy(final CommandLine commandLine) {
+        if (commandLine.hasOption(CLIManager.CHECKSUM_FAILURE_POLICY)) {
+            return MavenExecutionRequest.CHECKSUM_POLICY_FAIL;
+        } else if (commandLine.hasOption(CLIManager.CHECKSUM_WARNING_POLICY)) {
+            return MavenExecutionRequest.CHECKSUM_POLICY_WARN;
+        } else {
+            return null;
+        }
+    }
 
-        //
-        // Builder, concurrency and parallelism
-        //
-        // We preserve the existing methods for builder selection which is to look for various inputs in the threading
-        // configuration. We don't have an easy way to allow a pluggable builder to provide its own configuration
-        // parameters but this is sufficient for now. Ultimately we want components like Builders to provide a way to
-        // extend the command line to accept its own configuration parameters.
-        //
-        final String threadConfiguration = commandLine.hasOption(CLIManager.THREADS)
-                ? commandLine.getOptionValue(CLIManager.THREADS)
-                : null;
+    private void disableOnPresentOption(final CommandLine commandLine,
+            final String option,
+            final Consumer<Boolean> setting) {
+        if (commandLine.hasOption(option)) {
+            setting.accept(false);
+        }
+    }
 
-        if (threadConfiguration != null) {
-            //
-            // Default to the standard multithreaded builder
-            //
-            request.setBuilderId("multithreaded");
+    private void disableOnPresentOption(final CommandLine commandLine,
+            final char option,
+            final Consumer<Boolean> setting) {
+        disableOnPresentOption(commandLine, String.valueOf(option), setting);
+    }
 
-            if (threadConfiguration.contains("C")) {
-                request.setDegreeOfConcurrency(calculateDegreeOfConcurrencyWithCoreMultiplier(threadConfiguration));
-            } else {
-                request.setDegreeOfConcurrency(Integer.parseInt(threadConfiguration));
-            }
+    private void enableOnPresentOption(final CommandLine commandLine,
+            final String option,
+            final Consumer<Boolean> setting) {
+        if (commandLine.hasOption(option)) {
+            setting.accept(true);
         }
+    }
 
-        //
-        // Allow the builder to be overridden by the user if requested. The builders are now pluggable.
-        //
-        if (commandLine.hasOption(CLIManager.BUILDER)) {
-            request.setBuilderId(commandLine.getOptionValue(CLIManager.BUILDER));
+    private void enableOnPresentOption(final CommandLine commandLine,
+            final char option,
+            final Consumer<Boolean> setting) {
+        enableOnPresentOption(commandLine, String.valueOf(option), setting);
+    }
+
+    private void enableOnAbsentOption(final CommandLine commandLine,
+            final char option,
+            final Consumer<Boolean> setting) {
+        if (!commandLine.hasOption(option)) {
+            setting.accept(true);
         }
     }
 
-    static int calculateDegreeOfConcurrencyWithCoreMultiplier(String threadConfiguration) {
-        int procs = Runtime.getRuntime().availableProcessors();
-        return (int) (Float.parseFloat(threadConfiguration.replace("C", "")) * procs);
+    int calculateDegreeOfConcurrency(String threadConfiguration) {
+        if (threadConfiguration.endsWith("C")) {
+            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.");
+            }
+        }
     }
 
     static File resolveFile(File file, String workingDirectory) {
@@ -1280,15 +1302,10 @@ public class DaemonMavenCli {
         // are most dominant.
         // ----------------------------------------------------------------------
 
-        if (commandLine.hasOption(CLIManager.SET_SYSTEM_PROPERTY)) {
-            String[] defStrs = commandLine.getOptionValues(CLIManager.SET_SYSTEM_PROPERTY);
-
-            if (defStrs != null) {
-                for (String defStr : defStrs) {
-                    setCliProperty(defStr, userProperties);
-                }
-            }
-        }
+        final Properties userSpecifiedProperties = commandLine.getOptionProperties(
+                String.valueOf(CLIManager.SET_SYSTEM_PROPERTY));
+        userSpecifiedProperties.forEach(
+                (prop, value) -> setCliProperty((String) prop, (String) value, userProperties));
 
         SystemProperties.addSystemProperties(systemProperties);
 
@@ -1316,23 +1333,7 @@ public class DaemonMavenCli {
         }
     }
 
-    private static void setCliProperty(String property, Properties properties) {
-        String name;
-
-        String value;
-
-        int i = property.indexOf('=');
-
-        if (i <= 0) {
-            name = property.trim();
-
-            value = "true";
-        } else {
-            name = property.substring(0, i).trim();
-
-            value = property.substring(i + 1);
-        }
-
+    private static void setCliProperty(String name, String value, Properties properties) {
         properties.setProperty(name, value);
 
         // ----------------------------------------------------------------------
diff --git a/daemon/src/main/java/org/apache/maven/project/CachingProjectBuilder.java b/daemon/src/main/java/org/apache/maven/project/CachingProjectBuilder.java
index 9a919db..0dfe755 100644
--- a/daemon/src/main/java/org/apache/maven/project/CachingProjectBuilder.java
+++ b/daemon/src/main/java/org/apache/maven/project/CachingProjectBuilder.java
@@ -31,6 +31,7 @@ import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.stream.Collectors;
 import javax.inject.Inject;
 import javax.inject.Named;
 import javax.inject.Singleton;
@@ -41,6 +42,7 @@ import org.apache.maven.artifact.InvalidRepositoryException;
 import org.apache.maven.artifact.repository.ArtifactRepository;
 import org.apache.maven.artifact.repository.LegacyLocalRepositoryManager;
 import org.apache.maven.bridge.MavenRepositorySystem;
+import org.apache.maven.feature.Features;
 import org.apache.maven.model.Build;
 import org.apache.maven.model.Dependency;
 import org.apache.maven.model.DependencyManagement;
@@ -50,6 +52,7 @@ import org.apache.maven.model.Model;
 import org.apache.maven.model.Plugin;
 import org.apache.maven.model.Profile;
 import org.apache.maven.model.ReportPlugin;
+import org.apache.maven.model.building.ArtifactModelSource;
 import org.apache.maven.model.building.DefaultModelBuildingRequest;
 import org.apache.maven.model.building.DefaultModelProblem;
 import org.apache.maven.model.building.FileModelSource;
@@ -62,11 +65,15 @@ import org.apache.maven.model.building.ModelProblem;
 import org.apache.maven.model.building.ModelProcessor;
 import org.apache.maven.model.building.ModelSource;
 import org.apache.maven.model.building.StringModelSource;
+import org.apache.maven.model.building.TransformerContext;
+import org.apache.maven.model.building.TransformerContextBuilder;
 import org.apache.maven.model.resolution.ModelResolver;
 import org.apache.maven.repository.internal.ArtifactDescriptorUtils;
+import org.apache.maven.repository.internal.DefaultModelCache;
 import org.codehaus.plexus.logging.Logger;
 import org.codehaus.plexus.util.Os;
 import org.codehaus.plexus.util.StringUtils;
+import org.eclipse.aether.DefaultRepositorySystemSession;
 import org.eclipse.aether.RepositorySystemSession;
 import org.eclipse.aether.RequestTrace;
 import org.eclipse.aether.impl.RemoteRepositoryManager;
@@ -76,17 +83,12 @@ import org.eclipse.aether.repository.WorkspaceRepository;
 import org.eclipse.aether.resolution.ArtifactRequest;
 import org.eclipse.aether.resolution.ArtifactResult;
 import org.eclipse.sisu.Priority;
-import org.eclipse.sisu.Typed;
 
 /**
- * DefaultProjectBuilder
- *
- * File origin:
- * https://github.com/apache/maven/blob/maven-3.6.2/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java
+ * CachingProjectBuilder
  */
 @Named
 @Singleton
-@Typed(ProjectBuilder.class)
 @Priority(10)
 public class CachingProjectBuilder
         implements ProjectBuilder {
@@ -115,10 +117,7 @@ public class CachingProjectBuilder
     @Inject
     private ProjectDependenciesResolver dependencyResolver;
 
-    private final ModelCache modelCache = new ReactorModelCache();
-
-    public CachingProjectBuilder() {
-    }
+    private final ModelCache modelCache = DefaultModelCache.newInstance(new DefaultRepositorySystemSession());
 
     // ----------------------------------------------------------------------
     // MavenProjectBuilder Implementation
@@ -128,14 +127,14 @@ public class CachingProjectBuilder
     public ProjectBuildingResult build(File pomFile, ProjectBuildingRequest request)
             throws ProjectBuildingException {
         return build(pomFile, new FileModelSource(pomFile),
-                new InternalConfig(request, null, new SnapshotModelCache(getModelCache())));
+                new InternalConfig(request, null, null));
     }
 
     @Override
     public ProjectBuildingResult build(ModelSource modelSource, ProjectBuildingRequest request)
             throws ProjectBuildingException {
         return build(null, modelSource,
-                new InternalConfig(request, null, new SnapshotModelCache(getModelCache())));
+                new InternalConfig(request, null, null));
     }
 
     private ProjectBuildingResult build(File pomFile, ModelSource modelSource, InternalConfig config)
@@ -170,8 +169,7 @@ public class CachingProjectBuilder
                 } catch (ModelBuildingException e) {
                     result = e.getResult();
                     if (result == null || result.getEffectiveModel() == null) {
-                        throw (ProjectBuildingException) new ProjectBuildingException(e.getModelId(), e.getMessage(), pomFile)
-                                .initCause(e);
+                        throw new ProjectBuildingException(e.getModelId(), e.getMessage(), pomFile, e);
                     }
                     // validation error, continue project building and delay failing to help IDEs
                     error = e;
@@ -179,8 +177,8 @@ public class CachingProjectBuilder
 
                 modelProblems = result.getProblems();
 
-                initProject(project, Collections.<String, MavenProject> emptyMap(), true,
-                        result, new HashMap<File, Boolean>(), projectBuildingRequest);
+                initProject(project, Collections.emptyMap(), true,
+                        result, new HashMap<>(), projectBuildingRequest);
             } else if (projectBuildingRequest.isResolveDependencies()) {
                 projectBuildingHelper.selectProjectRealm(project);
             }
@@ -235,13 +233,7 @@ public class CachingProjectBuilder
     }
 
     private List<String> getProfileIds(List<Profile> profiles) {
-        List<String> ids = new ArrayList<>(profiles.size());
-
-        for (Profile profile : profiles) {
-            ids.add(profile.getId());
-        }
-
-        return ids;
+        return profiles.stream().map(Profile::getId).collect(Collectors.toList());
     }
 
     private ModelBuildingRequest getModelBuildingRequest(InternalConfig config) {
@@ -264,7 +256,11 @@ public class CachingProjectBuilder
         request.setUserProperties(configuration.getUserProperties());
         request.setBuildStartTime(configuration.getBuildStartTime());
         request.setModelResolver(resolver);
-        request.setModelCache(config.modelCache);
+        // this is a hint that we want to build 1 file, so don't cache. See MNG-7063
+        if (config.modelPool != null) {
+            request.setModelCache(new SnapshotModelCache(modelCache, DefaultModelCache.newInstance(config.session)));
+        }
+        request.setTransformerContextBuilder(config.transformerContextBuilder);
 
         return request;
     }
@@ -281,7 +277,7 @@ public class CachingProjectBuilder
         org.eclipse.aether.artifact.Artifact pomArtifact = RepositoryUtils.toArtifact(artifact);
         pomArtifact = ArtifactDescriptorUtils.toPomArtifact(pomArtifact);
 
-        InternalConfig config = new InternalConfig(request, null, new SnapshotModelCache(getModelCache()));
+        InternalConfig config = new InternalConfig(request, null, null);
 
         boolean localProject;
 
@@ -309,7 +305,13 @@ public class CachingProjectBuilder
             artifact.setResolved(true);
         }
 
-        return build(localProject ? pomFile : null, new FileModelSource(pomFile), config);
+        if (localProject) {
+            return build(pomFile, new FileModelSource(pomFile), config);
+        } else {
+            return build(null, new ArtifactModelSource(pomFile, artifact.getGroupId(), artifact.getArtifactId(),
+                    artifact.getVersion()),
+                    config);
+        }
     }
 
     private ModelSource createStubModelSource(Artifact artifact) {
@@ -334,26 +336,32 @@ public class CachingProjectBuilder
 
         List<InterimResult> interimResults = new ArrayList<>();
 
-        ReactorModelPool modelPool = new ReactorModelPool();
+        ReactorModelPool.Builder poolBuilder = new ReactorModelPool.Builder();
+        final ReactorModelPool modelPool = poolBuilder.build();
 
-        InternalConfig config = new InternalConfig(request, modelPool, new SnapshotModelCache(getModelCache()));
+        InternalConfig config = new InternalConfig(request, modelPool, modelBuilder.newTransformerContextBuilder());
 
-        Map<String, MavenProject> projectIndex = new HashMap<>(256);
+        Map<File, MavenProject> projectIndex = new HashMap<>(256);
 
-        boolean noErrors = build(results, interimResults, projectIndex, pomFiles, new LinkedHashSet<File>(), true, recursive,
-                config);
-
-        populateReactorModelPool(modelPool, interimResults);
+        // phase 1: get file Models from the reactor.
+        boolean noErrors = build(results, interimResults, projectIndex, pomFiles, new LinkedHashSet<>(), true, recursive,
+                config, poolBuilder);
 
         ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader();
 
         try {
-            noErrors = build(results, new ArrayList<MavenProject>(), projectIndex, interimResults, request,
-                    new HashMap<File, Boolean>(), config.session) && noErrors;
+            // Phase 2: get effective models from the reactor
+            noErrors = build(results, new ArrayList<>(), projectIndex, interimResults, request,
+                    new HashMap<>(), config.session) && noErrors;
         } finally {
             Thread.currentThread().setContextClassLoader(oldContextClassLoader);
         }
 
+        if (Features.buildConsumer(request.getUserProperties()).isActive()) {
+            request.getRepositorySession().getData().set(TransformerContext.KEY,
+                    config.transformerContextBuilder.build());
+        }
+
         if (!noErrors) {
             throw new ProjectBuildingException(results);
         }
@@ -363,14 +371,16 @@ public class CachingProjectBuilder
 
     @SuppressWarnings("checkstyle:parameternumber")
     private boolean build(List<ProjectBuildingResult> results, List<InterimResult> interimResults,
-            Map<String, MavenProject> projectIndex, List<File> pomFiles, Set<File> aggregatorFiles,
-            boolean isRoot, boolean recursive, InternalConfig config) {
+            Map<File, MavenProject> projectIndex, List<File> pomFiles, Set<File> aggregatorFiles,
+            boolean root, boolean recursive, InternalConfig config,
+            ReactorModelPool.Builder poolBuilder) {
         boolean noErrors = true;
 
         for (File pomFile : pomFiles) {
             aggregatorFiles.add(pomFile);
 
-            if (!build(results, interimResults, projectIndex, pomFile, aggregatorFiles, isRoot, recursive, config)) {
+            if (!build(results, interimResults, projectIndex, pomFile, aggregatorFiles, root, recursive, config,
+                    poolBuilder)) {
                 noErrors = false;
             }
 
@@ -382,18 +392,18 @@ public class CachingProjectBuilder
 
     @SuppressWarnings("checkstyle:parameternumber")
     private boolean build(List<ProjectBuildingResult> results, List<InterimResult> interimResults,
-            Map<String, MavenProject> projectIndex, File pomFile, Set<File> aggregatorFiles,
-            boolean isRoot, boolean recursive, InternalConfig config) {
+            Map<File, MavenProject> projectIndex, File pomFile, Set<File> aggregatorFiles,
+            boolean isRoot, boolean recursive, InternalConfig config,
+            ReactorModelPool.Builder poolBuilder) {
         boolean noErrors = true;
 
-        ModelBuildingRequest request = getModelBuildingRequest(config);
-
         MavenProject project = new MavenProject();
         project.setFile(pomFile);
 
-        request.setPomFile(pomFile);
-        request.setTwoPhaseBuilding(true);
-        request.setLocationTracking(true);
+        ModelBuildingRequest request = getModelBuildingRequest(config)
+                .setPomFile(pomFile)
+                .setTwoPhaseBuilding(true)
+                .setLocationTracking(true);
 
         DefaultModelBuildingListener listener = new DefaultModelBuildingListener(project, projectBuildingHelper,
                 config.request);
@@ -404,7 +414,7 @@ public class CachingProjectBuilder
             result = modelBuilder.build(request);
         } catch (ModelBuildingException e) {
             result = e.getResult();
-            if (result == null || result.getEffectiveModel() == null) {
+            if (result == null || result.getFileModel() == null) {
                 results.add(new DefaultProjectBuildingResult(e.getModelId(), pomFile, e.getProblems()));
 
                 return false;
@@ -414,25 +424,16 @@ public class CachingProjectBuilder
             noErrors = false;
         }
 
-        Model model = result.getEffectiveModel();
-        try {
-            // first pass: build without building parent.
-            initProject(project, projectIndex, false, result, new HashMap<File, Boolean>(0), config.request);
-        } catch (InvalidArtifactRTException iarte) {
-            result.getProblems().add(new DefaultModelProblem(null, ModelProblem.Severity.ERROR, null, model, -1, -1,
-                    iarte));
-        }
+        Model model = result.getFileModel().clone();
 
-        projectIndex.put(result.getModelIds().get(0), project);
+        poolBuilder.put(model.getPomFile().toPath(), model);
 
         InterimResult interimResult = new InterimResult(pomFile, request, result, listener, isRoot);
         interimResults.add(interimResult);
 
-        if (recursive && !model.getModules().isEmpty()) {
+        if (recursive) {
             File basedir = pomFile.getParentFile();
-
             List<File> moduleFiles = new ArrayList<>();
-
             for (String module : model.getModules()) {
                 if (StringUtils.isEmpty(module)) {
                     continue;
@@ -491,11 +492,13 @@ public class CachingProjectBuilder
             interimResult.modules = new ArrayList<>();
 
             if (!build(results, interimResult.modules, projectIndex, moduleFiles, aggregatorFiles, false,
-                    recursive, config)) {
+                    recursive, config, poolBuilder)) {
                 noErrors = false;
             }
         }
 
+        projectIndex.put(pomFile, project);
+
         return noErrors;
     }
 
@@ -524,17 +527,8 @@ public class CachingProjectBuilder
 
     }
 
-    private void populateReactorModelPool(ReactorModelPool reactorModelPool, List<InterimResult> interimResults) {
-        for (InterimResult interimResult : interimResults) {
-            Model model = interimResult.result.getEffectiveModel();
-            reactorModelPool.put(model.getGroupId(), model.getArtifactId(), model.getVersion(), model.getPomFile());
-
-            populateReactorModelPool(reactorModelPool, interimResult.modules);
-        }
-    }
-
     private boolean build(List<ProjectBuildingResult> results, List<MavenProject> projects,
-            Map<String, MavenProject> projectIndex, List<InterimResult> interimResults,
+            Map<File, MavenProject> projectIndex, List<InterimResult> interimResults,
             ProjectBuildingRequest request, Map<File, Boolean> profilesXmls,
             RepositorySystemSession session) {
         boolean noErrors = true;
@@ -569,9 +563,11 @@ public class CachingProjectBuilder
                 results.add(new DefaultProjectBuildingResult(project, result.getProblems(), resolutionResult));
             } catch (ModelBuildingException e) {
                 DefaultProjectBuildingResult result = null;
-                if (project == null) {
+                if (project == null || interimResult.result.getEffectiveModel() == null) {
                     result = new DefaultProjectBuildingResult(e.getModelId(), interimResult.pomFile, e.getProblems());
                 } else {
+                    project.setModel(interimResult.result.getEffectiveModel());
+
                     result = new DefaultProjectBuildingResult(project, e.getProblems(), null);
                 }
                 results.add(result);
@@ -584,14 +580,13 @@ public class CachingProjectBuilder
     }
 
     @SuppressWarnings("checkstyle:methodlength")
-    private void initProject(MavenProject project, Map<String, MavenProject> projects,
+    private void initProject(MavenProject project, Map<File, MavenProject> projects,
             boolean buildParentIfNotExisting, ModelBuildingResult result,
             Map<File, Boolean> profilesXmls, ProjectBuildingRequest projectBuildingRequest) {
         Model model = result.getEffectiveModel();
 
         project.setModel(model);
-        project.setOriginalModel(result.getRawModel());
-        project.setFile(model.getPomFile());
+        project.setOriginalModel(result.getFileModel());
 
         initParent(project, projects, buildParentIfNotExisting, result, projectBuildingRequest);
 
@@ -618,14 +613,6 @@ public class CachingProjectBuilder
             project.setInjectedProfileIds(modelId, getProfileIds(result.getActivePomProfiles(modelId)));
         }
 
-        String modelId = findProfilesXml(result, profilesXmls);
-        if (modelId != null) {
-            ModelProblem problem = new DefaultModelProblem("Detected profiles.xml alongside " + modelId
-                    + ", this file is no longer supported and was ignored" + ", please use the settings.xml instead",
-                    ModelProblem.Severity.WARNING, ModelProblem.Version.V30, model, -1, -1, null);
-            result.getProblems().add(problem);
-        }
-
         //
         // All the parts that were taken out of MavenProject for Maven 4.0.0
         //
@@ -747,7 +734,7 @@ public class CachingProjectBuilder
             try {
                 DeploymentRepository r = project.getDistributionManagement().getRepository();
                 if (!StringUtils.isEmpty(r.getId()) && !StringUtils.isEmpty(r.getUrl())) {
-                    ArtifactRepository repo = repositorySystem.buildArtifactRepository(r);
+                    ArtifactRepository repo = MavenRepositorySystem.buildArtifactRepository(r);
                     repositorySystem.injectProxy(projectBuildingRequest.getRepositorySession(),
                             Arrays.asList(repo));
                     repositorySystem.injectAuthentication(projectBuildingRequest.getRepositorySession(),
@@ -766,7 +753,7 @@ public class CachingProjectBuilder
             try {
                 DeploymentRepository r = project.getDistributionManagement().getSnapshotRepository();
                 if (!StringUtils.isEmpty(r.getId()) && !StringUtils.isEmpty(r.getUrl())) {
-                    ArtifactRepository repo = repositorySystem.buildArtifactRepository(r);
+                    ArtifactRepository repo = MavenRepositorySystem.buildArtifactRepository(r);
                     repositorySystem.injectProxy(projectBuildingRequest.getRepositorySession(),
                             Arrays.asList(repo));
                     repositorySystem.injectAuthentication(projectBuildingRequest.getRepositorySession(),
@@ -780,7 +767,7 @@ public class CachingProjectBuilder
         }
     }
 
-    private void initParent(MavenProject project, Map<String, MavenProject> projects, boolean buildParentIfNotExisting,
+    private void initParent(MavenProject project, Map<File, MavenProject> projects, boolean buildParentIfNotExisting,
             ModelBuildingResult result, ProjectBuildingRequest projectBuildingRequest) {
         Model parentModel = result.getModelIds().size() > 1 && !result.getModelIds().get(1).isEmpty()
                 ? result.getRawModel(result.getModelIds().get(1))
@@ -797,7 +784,7 @@ public class CachingProjectBuilder
             // org.apache.maven.its.mng4834:parent:0.1
             String parentModelId = result.getModelIds().get(1);
             File parentPomFile = result.getRawModel(parentModelId).getPomFile();
-            MavenProject parent = projects.get(parentModelId);
+            MavenProject parent = projects.get(parentPomFile);
             if (parent == null && buildParentIfNotExisting) {
                 //
                 // At this point the DefaultModelBuildingListener has fired and it populates the
@@ -872,28 +859,6 @@ public class CachingProjectBuilder
         return version;
     }
 
-    private String findProfilesXml(ModelBuildingResult result, Map<File, Boolean> profilesXmls) {
-        for (String modelId : result.getModelIds()) {
-            Model model = result.getRawModel(modelId);
-
-            File basedir = model.getProjectDirectory();
-            if (basedir == null) {
-                break;
-            }
-
-            Boolean profilesXml = profilesXmls.get(basedir);
-            if (profilesXml == null) {
-                profilesXml = new File(basedir, "profiles.xml").exists();
-                profilesXmls.put(basedir, profilesXml);
-            }
-            if (profilesXml) {
-                return modelId;
-            }
-        }
-
-        return null;
-    }
-
     /**
      * InternalConfig
      */
@@ -907,21 +872,20 @@ public class CachingProjectBuilder
 
         private final ReactorModelPool modelPool;
 
-        private final ModelCache modelCache;
+        private final TransformerContextBuilder transformerContextBuilder;
 
-        InternalConfig(ProjectBuildingRequest request, ReactorModelPool modelPool, ModelCache modelCache) {
+        InternalConfig(ProjectBuildingRequest request, ReactorModelPool modelPool,
+                TransformerContextBuilder transformerContextBuilder) {
             this.request = request;
             this.modelPool = modelPool;
-            this.modelCache = modelCache;
+            this.transformerContextBuilder = transformerContextBuilder;
+
             session = LegacyLocalRepositoryManager.overlay(request.getLocalRepository(), request.getRepositorySession(),
                     repoSystem);
             repositories = RepositoryUtils.toRepos(request.getRemoteRepositories());
-        }
 
-    }
+        }
 
-    private ModelCache getModelCache() {
-        return this.modelCache;
     }
 
 }
diff --git a/daemon/src/main/java/org/apache/maven/project/SnapshotModelCache.java b/daemon/src/main/java/org/apache/maven/project/SnapshotModelCache.java
index b4aee74..c80f69e 100644
--- a/daemon/src/main/java/org/apache/maven/project/SnapshotModelCache.java
+++ b/daemon/src/main/java/org/apache/maven/project/SnapshotModelCache.java
@@ -22,9 +22,9 @@ public class SnapshotModelCache implements ModelCache {
     private final ModelCache globalCache;
     private final ModelCache reactorCache;
 
-    public SnapshotModelCache(ModelCache globalCache) {
+    public SnapshotModelCache(ModelCache globalCache, ModelCache reactorCache) {
         this.globalCache = globalCache;
-        this.reactorCache = new ReactorModelCache();
+        this.reactorCache = reactorCache;
     }
 
     @Override
diff --git a/daemon/src/main/java/org/mvndaemon/mvnd/execution/BuildResumptionAnalyzer.java b/daemon/src/main/java/org/mvndaemon/mvnd/execution/BuildResumptionAnalyzer.java
deleted file mode 100644
index 07d0fc7..0000000
--- a/daemon/src/main/java/org/mvndaemon/mvnd/execution/BuildResumptionAnalyzer.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright 2019 the original author or authors.
- *
- * Licensed 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.
- */
-package org.mvndaemon.mvnd.execution;
-
-/*
- * 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 java.util.Optional;
-import org.apache.maven.execution.MavenExecutionResult;
-
-/**
- * Instances of this class are responsible for determining whether it makes sense to "resume" a build (i.e., using
- * the {@code --resume} flag.
- */
-public interface BuildResumptionAnalyzer {
-    /**
-     * Construct an instance of {@link BuildResumptionData} based on the outcome of the current Maven build.
-     * 
-     * @param  result Outcome of the current Maven build.
-     * @return        A {@link BuildResumptionData} instance or {@link Optional#empty()} if resuming the build is not
-     *                possible.
-     */
-    Optional<BuildResumptionData> determineBuildResumptionData(final MavenExecutionResult result);
-}
diff --git a/daemon/src/main/java/org/mvndaemon/mvnd/execution/BuildResumptionData.java b/daemon/src/main/java/org/mvndaemon/mvnd/execution/BuildResumptionData.java
deleted file mode 100644
index cbe3eb4..0000000
--- a/daemon/src/main/java/org/mvndaemon/mvnd/execution/BuildResumptionData.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright 2019 the original author or authors.
- *
- * Licensed 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.
- */
-package org.mvndaemon.mvnd.execution;
-
-/*
- * 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 java.util.List;
-
-/**
- * This class holds the information required to enable resuming a Maven build with {@code --resume}.
- */
-public class BuildResumptionData {
-    /**
-     * The list of projects that remain to be built.
-     */
-    private final List<String> remainingProjects;
-
-    public BuildResumptionData(final List<String> remainingProjects) {
-        this.remainingProjects = remainingProjects;
-    }
-
-    /**
-     * Returns the projects that still need to be built when resuming.
-     * 
-     * @return A list containing the group and artifact id of the projects.
-     */
-    public List<String> getRemainingProjects() {
-        return this.remainingProjects;
-    }
-
-}
diff --git a/daemon/src/main/java/org/mvndaemon/mvnd/execution/BuildResumptionDataRepository.java b/daemon/src/main/java/org/mvndaemon/mvnd/execution/BuildResumptionDataRepository.java
deleted file mode 100644
index 52b998e..0000000
--- a/daemon/src/main/java/org/mvndaemon/mvnd/execution/BuildResumptionDataRepository.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright 2019 the original author or authors.
- *
- * Licensed 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.
- */
-package org.mvndaemon.mvnd.execution;
-
-/*
- * 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.execution.MavenExecutionRequest;
-import org.apache.maven.project.MavenProject;
-
-/**
- * Instances of this interface retrieve and store data for the --resume / -r feature. This data is used to ensure newer
- * builds of the same project, that have the -r command-line flag, skip successfully built projects during earlier
- * invocations of Maven.
- */
-public interface BuildResumptionDataRepository {
-    /**
-     * 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  rootProject                         The root project that is being built.
-     * @param  buildResumptionData                 Information needed to resume the build.
-     * @throws BuildResumptionPersistenceException When an error occurs while persisting data.
-     */
-    void persistResumptionData(final MavenProject rootProject, final BuildResumptionData buildResumptionData)
-            throws BuildResumptionPersistenceException;
-
-    /**
-     * 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);
-
-    /**
-     * Removes previously stored resumption data.
-     * 
-     * @param rootProject The root project that is being built.
-     */
-    void removeResumptionData(final MavenProject rootProject);
-
-}
diff --git a/daemon/src/main/java/org/mvndaemon/mvnd/execution/BuildResumptionPersistenceException.java b/daemon/src/main/java/org/mvndaemon/mvnd/execution/BuildResumptionPersistenceException.java
deleted file mode 100644
index c80bede..0000000
--- a/daemon/src/main/java/org/mvndaemon/mvnd/execution/BuildResumptionPersistenceException.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2019 the original author or authors.
- *
- * Licensed 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.
- */
-package org.mvndaemon.mvnd.execution;
-
-/*
- * 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.
- */
-
-/**
- * This exception will be thrown when something fails while persisting build resumption data.
- * 
- * @see BuildResumptionDataRepository#persistResumptionData
- */
-public class BuildResumptionPersistenceException extends Exception {
-    public BuildResumptionPersistenceException(String message, Throwable cause) {
-        super(message, cause);
-    }
-}
diff --git a/daemon/src/main/java/org/mvndaemon/mvnd/execution/DefaultBuildResumptionAnalyzer.java b/daemon/src/main/java/org/mvndaemon/mvnd/execution/DefaultBuildResumptionAnalyzer.java
deleted file mode 100644
index 844169f..0000000
--- a/daemon/src/main/java/org/mvndaemon/mvnd/execution/DefaultBuildResumptionAnalyzer.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright 2019 the original author or authors.
- *
- * Licensed 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.
- */
-package org.mvndaemon.mvnd.execution;
-
-/*
- * 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 java.util.List;
-import java.util.Optional;
-import java.util.stream.Collectors;
-import javax.inject.Named;
-import javax.inject.Singleton;
-import org.apache.maven.execution.BuildFailure;
-import org.apache.maven.execution.BuildSuccess;
-import org.apache.maven.execution.MavenExecutionResult;
-import org.apache.maven.project.MavenProject;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Default implementation of {@link BuildResumptionAnalyzer}.
- */
-@Named
-@Singleton
-public class DefaultBuildResumptionAnalyzer implements BuildResumptionAnalyzer {
-    private static final Logger LOGGER = LoggerFactory.getLogger(DefaultBuildResumptionAnalyzer.class);
-
-    @Override
-    public Optional<BuildResumptionData> determineBuildResumptionData(final MavenExecutionResult result) {
-        if (!result.hasExceptions()) {
-            return Optional.empty();
-        }
-
-        List<MavenProject> sortedProjects = result.getTopologicallySortedProjects();
-
-        boolean hasNoSuccess = sortedProjects.stream()
-                .noneMatch(project -> result.getBuildSummary(project) instanceof BuildSuccess);
-
-        if (hasNoSuccess) {
-            return Optional.empty();
-        }
-
-        List<String> remainingProjects = sortedProjects.stream()
-                .filter(project -> result.getBuildSummary(project) == null
-                        || result.getBuildSummary(project) instanceof BuildFailure)
-                .map(project -> project.getGroupId() + ":" + project.getArtifactId())
-                .collect(Collectors.toList());
-
-        if (remainingProjects.isEmpty()) {
-            LOGGER.info("No remaining projects found, resuming the build would not make sense.");
-            return Optional.empty();
-        }
-
-        return Optional.of(new BuildResumptionData(remainingProjects));
-    }
-
-}
diff --git a/daemon/src/main/java/org/mvndaemon/mvnd/execution/DefaultBuildResumptionDataRepository.java b/daemon/src/main/java/org/mvndaemon/mvnd/execution/DefaultBuildResumptionDataRepository.java
deleted file mode 100644
index 8bc3817..0000000
--- a/daemon/src/main/java/org/mvndaemon/mvnd/execution/DefaultBuildResumptionDataRepository.java
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * Copyright 2019 the original author or authors.
- *
- * Licensed 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.
- */
-package org.mvndaemon.mvnd.execution;
-
-/*
- * 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 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.Properties;
-import java.util.stream.Stream;
-import javax.inject.Named;
-import javax.inject.Singleton;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.maven.execution.MavenExecutionRequest;
-import org.apache.maven.project.MavenProject;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * This implementation of {@link BuildResumptionDataRepository} persists information in a properties file. The file is
- * stored in the build output directory under the Maven execution root.
- */
-@Named
-@Singleton
-public class DefaultBuildResumptionDataRepository implements BuildResumptionDataRepository {
-    private static final String RESUME_PROPERTIES_FILENAME = "resume.properties";
-    private static final String REMAINING_PROJECTS = "remainingProjects";
-    private static final String PROPERTY_DELIMITER = ", ";
-    private static final Logger LOGGER = LoggerFactory.getLogger(DefaultBuildResumptionDataRepository.class);
-
-    @Override
-    public void persistResumptionData(MavenProject rootProject, BuildResumptionData buildResumptionData)
-            throws BuildResumptionPersistenceException {
-        Path directory = Paths.get(rootProject.getBuild().getDirectory());
-        persistResumptionData(directory, buildResumptionData);
-    }
-
-    public void persistResumptionData(Path directory, BuildResumptionData buildResumptionData)
-            throws BuildResumptionPersistenceException {
-        Properties properties = convertToProperties(buildResumptionData);
-
-        Path resumeProperties = directory.resolve(RESUME_PROPERTIES_FILENAME);
-        try {
-            Files.createDirectories(resumeProperties.getParent());
-            try (Writer writer = Files.newBufferedWriter(resumeProperties)) {
-                properties.store(writer, null);
-            }
-        } catch (IOException e) {
-            String message = "Could not create " + RESUME_PROPERTIES_FILENAME + " file.";
-            throw new BuildResumptionPersistenceException(message, e);
-        }
-    }
-
-    private Properties convertToProperties(final BuildResumptionData buildResumptionData) {
-        Properties properties = new Properties();
-
-        String value = String.join(PROPERTY_DELIMITER, buildResumptionData.getRemainingProjects());
-        properties.setProperty(REMAINING_PROJECTS, value);
-
-        return properties;
-    }
-
-    @Override
-    public void applyResumptionData(MavenExecutionRequest request, MavenProject rootProject) {
-        Path directory = Paths.get(rootProject.getBuild().getDirectory());
-        applyResumptionData(request, directory);
-    }
-
-    public void applyResumptionData(MavenExecutionRequest request, Path directory) {
-        Properties properties = loadResumptionFile(directory);
-        applyResumptionProperties(request, properties);
-    }
-
-    @Override
-    public void removeResumptionData(MavenProject rootProject) {
-        Path directory = Paths.get(rootProject.getBuild().getDirectory());
-        removeResumptionData(directory);
-    }
-
-    public void removeResumptionData(Path directory) {
-        Path resumeProperties = directory.resolve(RESUME_PROPERTIES_FILENAME);
-        try {
-            Files.deleteIfExists(resumeProperties);
-        } catch (IOException e) {
-            LOGGER.warn("Could not delete {} file. ", RESUME_PROPERTIES_FILENAME, e);
-        }
-    }
-
-    private Properties loadResumptionFile(Path rootBuildDirectory) {
-        Properties properties = new Properties();
-        Path path = rootBuildDirectory.resolve(RESUME_PROPERTIES_FILENAME);
-        if (!Files.exists(path)) {
-            LOGGER.warn("The {} file does not exist. The --resume / -r feature will not work.", path);
-            return properties;
-        }
-
-        try (Reader reader = Files.newBufferedReader(path)) {
-            properties.load(reader);
-        } catch (IOException e) {
-            LOGGER.warn("Unable to read {}. The --resume / -r feature will not work.", path);
-        }
-
-        return properties;
-    }
-
-    // This method is made package-private for testing purposes
-    void applyResumptionProperties(MavenExecutionRequest request, Properties properties) {
-        if (properties.containsKey(REMAINING_PROJECTS)
-                && StringUtils.isEmpty(request.getResumeFrom())) {
-            String propertyValue = properties.getProperty(REMAINING_PROJECTS);
-            Stream.of(propertyValue.split(PROPERTY_DELIMITER))
-                    .filter(StringUtils::isNotEmpty)
-                    .forEach(request.getSelectedProjects()::add);
-            LOGGER.info("Resuming from {} due to the --resume / -r feature.", propertyValue);
-        }
-    }
-}
diff --git a/daemon/src/main/java/org/mvndaemon/mvnd/plugin/CachingPluginVersionResolver.java b/daemon/src/main/java/org/mvndaemon/mvnd/plugin/CachingPluginVersionResolver.java
index 56d6398..4ecb450 100644
--- a/daemon/src/main/java/org/mvndaemon/mvnd/plugin/CachingPluginVersionResolver.java
+++ b/daemon/src/main/java/org/mvndaemon/mvnd/plugin/CachingPluginVersionResolver.java
@@ -19,15 +19,20 @@ import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
+import javax.inject.Inject;
 import javax.inject.Named;
 import javax.inject.Singleton;
+import org.apache.maven.artifact.repository.metadata.io.MetadataReader;
+import org.apache.maven.plugin.MavenPluginManager;
 import org.apache.maven.plugin.version.PluginVersionRequest;
 import org.apache.maven.plugin.version.PluginVersionResolutionException;
 import org.apache.maven.plugin.version.PluginVersionResolver;
 import org.apache.maven.plugin.version.PluginVersionResult;
 import org.apache.maven.plugin.version.internal.DefaultPluginVersionResolver;
+import org.eclipse.aether.RepositorySystem;
 import org.eclipse.aether.SessionData;
 import org.eclipse.aether.repository.RemoteRepository;
+import org.eclipse.aether.version.VersionScheme;
 import org.eclipse.sisu.Priority;
 import org.eclipse.sisu.Typed;
 
@@ -39,6 +44,12 @@ public class CachingPluginVersionResolver extends DefaultPluginVersionResolver {
 
     private static final Object CACHE_KEY = new Object();
 
+    @Inject
+    public CachingPluginVersionResolver(RepositorySystem repositorySystem, MetadataReader metadataReader,
+            MavenPluginManager pluginManager, VersionScheme versionScheme) {
+        super(repositorySystem, metadataReader, pluginManager, versionScheme);
+    }
+
     @Override
     public PluginVersionResult resolve(PluginVersionRequest request) throws PluginVersionResolutionException {
         Map<String, PluginVersionResult> cache = getCache(request.getRepositorySession().getData());
diff --git a/daemon/src/main/java/org/mvndaemon/mvnd/plugin/CliMavenPluginManager.java b/daemon/src/main/java/org/mvndaemon/mvnd/plugin/CliMavenPluginManager.java
index 9edb2b8..78ac8c8 100644
--- a/daemon/src/main/java/org/mvndaemon/mvnd/plugin/CliMavenPluginManager.java
+++ b/daemon/src/main/java/org/mvndaemon/mvnd/plugin/CliMavenPluginManager.java
@@ -47,13 +47,11 @@ import org.apache.maven.classrealm.ClassRealmManager;
 import org.apache.maven.execution.MavenSession;
 import org.apache.maven.execution.scope.internal.MojoExecutionScopeModule;
 import org.apache.maven.model.Plugin;
-import org.apache.maven.monitor.logging.DefaultLog;
 import org.apache.maven.plugin.ContextEnabled;
 import org.apache.maven.plugin.DebugConfigurationListener;
 import org.apache.maven.plugin.ExtensionRealmCache;
 import org.apache.maven.plugin.InvalidPluginDescriptorException;
 import org.apache.maven.plugin.MavenPluginManager;
-import org.apache.maven.plugin.MavenPluginValidator;
 import org.apache.maven.plugin.Mojo;
 import org.apache.maven.plugin.MojoExecution;
 import org.apache.maven.plugin.MojoNotFoundException;
@@ -72,6 +70,7 @@ import org.apache.maven.plugin.descriptor.MojoDescriptor;
 import org.apache.maven.plugin.descriptor.Parameter;
 import org.apache.maven.plugin.descriptor.PluginDescriptor;
 import org.apache.maven.plugin.descriptor.PluginDescriptorBuilder;
+import org.apache.maven.plugin.internal.MojoLogWrapper;
 import org.apache.maven.plugin.internal.PluginDependenciesResolver;
 import org.apache.maven.plugin.version.DefaultPluginVersionRequest;
 import org.apache.maven.plugin.version.PluginVersionRequest;
@@ -97,7 +96,6 @@ import org.codehaus.plexus.component.repository.exception.ComponentLookupExcepti
 import org.codehaus.plexus.configuration.PlexusConfiguration;
 import org.codehaus.plexus.configuration.PlexusConfigurationException;
 import org.codehaus.plexus.configuration.xml.XmlPlexusConfiguration;
-import org.codehaus.plexus.logging.Logger;
 import org.codehaus.plexus.logging.LoggerManager;
 import org.codehaus.plexus.util.ReaderFactory;
 import org.codehaus.plexus.util.StringUtils;
@@ -112,6 +110,8 @@ import org.eclipse.sisu.Priority;
 import org.eclipse.sisu.Typed;
 import org.mvndaemon.mvnd.cache.invalidating.InvalidatingPluginDescriptorCache;
 import org.mvndaemon.mvnd.cache.invalidating.InvalidatingPluginRealmCache;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /*
  * gnodet: This file is based on maven DefaultMavenPluginManager and changed in order
@@ -143,8 +143,7 @@ public class CliMavenPluginManager
      */
     public static final String KEY_EXTENSIONS_REALMS = CliMavenPluginManager.class.getName() + "/extensionsRealms";
 
-    @Inject
-    private Logger logger;
+    private final Logger logger = LoggerFactory.getLogger(getClass());
 
     @Inject
     private LoggerManager loggerManager;
@@ -236,13 +235,12 @@ public class CliMavenPluginManager
             throw new PluginDescriptorParsingException(plugin, pluginFile.getAbsolutePath(), e);
         }
 
-        MavenPluginValidator validator = new MavenPluginValidator(pluginArtifact);
-
-        validator.validate(pluginDescriptor);
+        List<String> errors = new ArrayList<>();
+        validate(pluginArtifact, pluginDescriptor, errors);
 
-        if (validator.hasErrors()) {
+        if (!errors.isEmpty()) {
             throw new InvalidPluginDescriptorException(
-                    "Invalid plugin descriptor for " + plugin.getId() + " (" + pluginFile + ")", validator.getErrors());
+                    "Invalid plugin descriptor for " + plugin.getId() + " (" + pluginFile + ")", errors);
         }
 
         pluginDescriptor.setPluginArtifact(pluginArtifact);
@@ -250,6 +248,20 @@ public class CliMavenPluginManager
         return pluginDescriptor;
     }
 
+    private void validate(Artifact pluginArtifact, PluginDescriptor pluginDescriptor, List<String> errors) {
+        if (!pluginArtifact.getGroupId().equals(pluginDescriptor.getGroupId())) {
+            errors.add("Plugin's descriptor contains the wrong group ID: " + pluginDescriptor.getGroupId());
+        }
+
+        if (!pluginArtifact.getArtifactId().equals(pluginDescriptor.getArtifactId())) {
+            errors.add("Plugin's descriptor contains the wrong artifact ID: " + pluginDescriptor.getArtifactId());
+        }
+
+        if (!pluginArtifact.getBaseVersion().equals(pluginDescriptor.getVersion())) {
+            errors.add("Plugin's descriptor contains the wrong version: " + pluginDescriptor.getVersion());
+        }
+    }
+
     private String getPluginDescriptorLocation() {
         return "META-INF/maven/plugin.xml";
     }
@@ -516,8 +528,8 @@ public class CliMavenPluginManager
             }
 
             if (mojo instanceof Mojo) {
-                Logger mojoLogger = loggerManager.getLoggerForComponent(mojoDescriptor.getImplementation());
-                ((Mojo) mojo).setLog(new DefaultLog(mojoLogger));
+                org.slf4j.Logger mojoLogger = LoggerFactory.getLogger(mojoDescriptor.getImplementation());
+                ((Mojo) mojo).setLog(new MojoLogWrapper(mojoLogger));
             }
 
             Xpp3Dom dom = mojoExecution.getConfiguration();
diff --git a/daemon/src/main/resources/META-INF/maven/extension.xml b/daemon/src/main/resources/META-INF/maven/extension.xml
index 71cacc7..bf06d2f 100644
--- a/daemon/src/main/resources/META-INF/maven/extension.xml
+++ b/daemon/src/main/resources/META-INF/maven/extension.xml
@@ -21,5 +21,10 @@ under the License.
 <extension>
     <exportedPackages>
         <exportedPackage>org.apache.commons.logging.*</exportedPackage>
+        <exportedPackage>org.codehaus.plexus.components.interactivity.*</exportedPackage>
     </exportedPackages>
+
+    <exportedArtifacts>
+        <exportedArtifact>org.codehaus.plexus:plexus-interactivity-api</exportedArtifact>
+    </exportedArtifacts>
 </extension>
diff --git a/dist/src/main/provisio/maven-distro.xml b/dist/src/main/provisio/maven-distro.xml
index 06b4281..145e2f8 100644
--- a/dist/src/main/provisio/maven-distro.xml
+++ b/dist/src/main/provisio/maven-distro.xml
@@ -20,12 +20,13 @@
     <artifactSet to="/mvn">
         <artifact id="org.apache.maven:apache-maven:tar.gz:bin">
             <unpack useRoot="false"
-                    excludes="lib/*slf4j*,conf/logging/*,lib/maven-slf4j-provider*,bin/mvn*,lib/jansi-*.jar,lib/jansi-native/*,lib/maven-resolver-api-*,lib/maven-resolver-impl-*,lib/maven-resolver-spi-*,lib/maven-resolver-util-*,lib/maven-resolver-connector-*,lib/maven-resolver-transport-*"/>
+                    excludes="conf/logging/*,lib/maven-slf4j-provider*,bin/mvn*,lib/jansi-*.jar,lib/jansi-native/*,lib/plexus-utils-3.*"/>
         </artifact>
     </artifactSet>
 
     <artifactSet to="/mvn/lib">
         <exclusion id="javax.annotation:javax.annotation-api"/>
+        <exclusion id="org.codehaus.plexus:plexus-utils"/>
         <artifact id="org.apache.maven.resolver:maven-resolver-api"/>
         <artifact id="org.apache.maven.resolver:maven-resolver-impl"/>
         <artifact id="org.apache.maven.resolver:maven-resolver-spi"/>
@@ -38,6 +39,7 @@
         <exclusion id="org.slf4j:slf4j-api"/>
         <artifact id="org.apache.maven.daemon:mvnd-daemon:${project.version}">
             <exclusion id="org.codehaus.plexus:plexus-classworlds"/>
+            <exclusion id="org.codehaus.plexus:plexus-utils"/>
             <exclusion id="*:cdi-api"/>
             <exclusion id="*:commons-cli"/>
             <exclusion id="*:commons-io"/>
@@ -45,7 +47,10 @@
             <exclusion id="*:guava"/>
             <exclusion id="*:guice"/>
             <exclusion id="*:javax.inject"/>
+            <exclusion id="*:jcl-over-slf4j"/>
+            <exclusion id="*:jul-to-slf4j"/>
             <exclusion id="*:jsr250-api"/>
+            <exclusion id="*:log4j-over-slf4j"/>
             <exclusion id="*:maven-artifact"/>
             <exclusion id="*:maven-builder-support"/>
             <exclusion id="*:maven-core"/>
@@ -70,6 +75,7 @@
             <exclusion id="*:plexus-sec-dispatcher"/>
             <exclusion id="*:plexus-utils"/>
             <exclusion id="*:plexus-container-default"/>
+            <exclusion id="*:slf4j-api"/>
         </artifact>
         <artifact id="org.apache.maven.daemon:mvnd-client:${project.version}">
             <exclusion id="*:*"/>
diff --git a/integration-tests/src/test/java/org/mvndaemon/mvnd/it/InteractiveTest.java b/integration-tests/src/test/java/org/mvndaemon/mvnd/it/InteractiveTest.java
index a6d6e2e..5cf28af 100644
--- a/integration-tests/src/test/java/org/mvndaemon/mvnd/it/InteractiveTest.java
+++ b/integration-tests/src/test/java/org/mvndaemon/mvnd/it/InteractiveTest.java
@@ -19,6 +19,7 @@ import java.io.IOException;
 import javax.inject.Inject;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.Timeout;
 import org.mvndaemon.mvnd.assertj.TestClientOutput;
 import org.mvndaemon.mvnd.client.Client;
 import org.mvndaemon.mvnd.client.DaemonParameters;
@@ -27,6 +28,7 @@ import org.mvndaemon.mvnd.common.Message.Prompt;
 import org.mvndaemon.mvnd.junit.MvndTest;
 
 @MvndTest(projectDir = "src/test/projects/single-module")
+@Timeout(300)
 public class InteractiveTest {
 
     @Inject
diff --git a/integration-tests/src/test/java/org/mvndaemon/mvnd/it/NewManagedModuleNativeIT.java b/integration-tests/src/test/java/org/mvndaemon/mvnd/it/NewManagedModuleNativeIT.java
index 21e6897..e809bc4 100644
--- a/integration-tests/src/test/java/org/mvndaemon/mvnd/it/NewManagedModuleNativeIT.java
+++ b/integration-tests/src/test/java/org/mvndaemon/mvnd/it/NewManagedModuleNativeIT.java
@@ -61,6 +61,10 @@ public class NewManagedModuleNativeIT {
         registry.awaitIdle(d.getId());
 
         /* Do the changes */
+        System.gc();
+        Thread.sleep(100);
+        System.gc();
+
         final Path srcDir = parentDir.resolve("../changes").normalize();
         try (Stream<Path> files = Files.walk(srcDir)) {
             files.forEach(source -> {
diff --git a/pom.xml b/pom.xml
index 4c4b5ad..667a66e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -64,8 +64,8 @@
         <jline.version>3.21.0</jline.version>
         <junit.jupiter.version>5.7.2</junit.jupiter.version>
         <logback.version>1.2.10</logback.version>
-        <maven.version>3.8.6</maven.version>
-        <maven.resolver.version>1.7.3</maven.resolver.version>
+        <maven.version>4.0.0-alpha-2</maven.version>
+        <maven.resolver.version>1.8.2</maven.resolver.version>
         <slf4j.version>1.7.35</slf4j.version>
         <sisu.version>0.3.5</sisu.version>
 
@@ -412,6 +412,13 @@ limitations under the License.</inlineHeader>
                         <skip>${format.skip}</skip>
                         <cachedir>${project.build.directory}/cache</cachedir>
                     </configuration>
+                    <dependencies>
+                        <dependency>
+                            <groupId>org.apache.maven</groupId>
+                            <artifactId>plexus-utils</artifactId>
+                            <version>${maven.version}</version>
+                        </dependency>
+                    </dependencies>
                 </plugin>
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>