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 95b40a3d Provide distributions for both maven 3.9.x and 4.0.x (#796)
95b40a3d is described below
commit 95b40a3d8a6adf91686234ebb39281ff0c935fea
Author: Guillaume Nodet <[email protected]>
AuthorDate: Wed Mar 8 00:03:49 2023 +0100
Provide distributions for both maven 3.9.x and 4.0.x (#796)
---
daemon-m39/pom.xml | 50 ++
.../java/org/apache/maven/cli/DaemonMavenCli.java | 490 ++++++-------
.../apache/maven/project/SnapshotModelCache.java | 15 +-
.../maven/project/SnapshotModelCacheFactory.java | 13 +-
.../InvalidatingPluginDescriptorCache.java | 0
.../invalidating/InvalidatingPluginRealmCache.java | 6 +-
.../InvalidatingRealmCacheEventSpy.java | 0
.../mvnd/execution/BuildResumptionAnalyzer.java | 57 ++
.../mvnd/execution/BuildResumptionData.java | 62 ++
.../execution/BuildResumptionDataRepository.java | 74 ++
.../BuildResumptionPersistenceException.java | 49 ++
.../execution/DefaultBuildResumptionAnalyzer.java | 90 +++
.../DefaultBuildResumptionDataRepository.java | 155 ++++
.../mvnd/plugin/CachingPluginVersionResolver.java | 14 -
.../mvnd/plugin/CliMavenPluginManager.java | 779 +++++++++++++++++++++
.../plugin/ValidatingConfigurationListener.java | 82 +++
daemon-m40/pom.xml | 50 ++
.../java/org/apache/maven/cli/DaemonMavenCli.java | 10 +-
.../apache/maven/project/SnapshotModelCache.java | 6 +-
.../maven/project/SnapshotModelCacheFactory.java | 0
.../org/apache/maven/settings/SettingsUtilsV4.java | 0
.../InvalidatingPluginDescriptorCache.java | 0
.../invalidating/InvalidatingPluginRealmCache.java | 0
.../InvalidatingRealmCacheEventSpy.java | 0
.../mvnd/plugin/CachingPluginVersionResolver.java | 0
daemon/pom.xml | 2 +-
.../main/java/org/apache/maven/cli/DaemonCli.java | 37 +
.../java/org/mvndaemon/mvnd/daemon/Server.java | 10 +-
{dist => dist-m39}/pom.xml | 10 +-
.../src/main/provisio/maven-distro.xml | 17 +-
{dist => dist-m40}/pom.xml | 10 +-
.../src/main/provisio/maven-distro.xml | 15 +-
dist/src/main/resources/platform-darwin-aarch64 | 13 +
dist/src/main/resources/platform-darwin-amd64 | 13 +
dist/src/main/resources/platform-linux-amd64 | 13 +
dist/src/main/resources/platform-windows-amd64 | 13 +
integration-tests/pom.xml | 91 ++-
pom.xml | 25 +-
38 files changed, 1955 insertions(+), 316 deletions(-)
diff --git a/daemon-m39/pom.xml b/daemon-m39/pom.xml
new file mode 100644
index 00000000..62ec50a0
--- /dev/null
+++ b/daemon-m39/pom.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ 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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.apache.maven.daemon</groupId>
+ <artifactId>mvnd</artifactId>
+ <version>1.0.0-m5-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>mvnd-daemon-m39</artifactId>
+
+ <packaging>jar</packaging>
+ <name>Maven Daemon - Daemon 3.9.x specifics</name>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.maven</groupId>
+ <artifactId>maven-core</artifactId>
+ <version>${maven3.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.maven</groupId>
+ <artifactId>maven-embedder</artifactId>
+ <version>${maven3.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.maven.daemon</groupId>
+ <artifactId>mvnd-daemon</artifactId>
+ </dependency>
+ </dependencies>
+
+</project>
diff --git a/daemon/src/main/java/org/apache/maven/cli/DaemonMavenCli.java
b/daemon-m39/src/main/java/org/apache/maven/cli/DaemonMavenCli.java
similarity index 81%
copy from daemon/src/main/java/org/apache/maven/cli/DaemonMavenCli.java
copy to daemon-m39/src/main/java/org/apache/maven/cli/DaemonMavenCli.java
index 940d6180..feac407e 100644
--- a/daemon/src/main/java/org/apache/maven/cli/DaemonMavenCli.java
+++ b/daemon-m39/src/main/java/org/apache/maven/cli/DaemonMavenCli.java
@@ -23,6 +23,8 @@ 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;
@@ -34,7 +36,7 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
-import java.util.function.Consumer;
+import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@@ -44,7 +46,6 @@ import com.google.inject.AbstractModule;
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,14 +62,17 @@ 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.*;
+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.scope.internal.MojoExecutionScopeModule;
import org.apache.maven.extension.internal.CoreExports;
-import org.apache.maven.extension.internal.CoreExportsProvider;
import org.apache.maven.extension.internal.CoreExtensionEntry;
import org.apache.maven.lifecycle.LifecycleExecutionException;
import org.apache.maven.model.building.ModelProcessor;
import org.apache.maven.plugin.ExtensionRealmCache;
+import org.apache.maven.plugin.MavenPluginManager;
import org.apache.maven.plugin.PluginArtifactsCache;
import org.apache.maven.plugin.PluginRealmCache;
import org.apache.maven.plugin.version.PluginVersionResolver;
@@ -98,11 +102,15 @@ 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;
import org.mvndaemon.mvnd.logging.smart.LoggingOutputStream;
import org.mvndaemon.mvnd.plugin.CachingPluginVersionResolver;
+import org.mvndaemon.mvnd.plugin.CliMavenPluginManager;
import org.mvndaemon.mvnd.transfer.DaemonMavenTransferListener;
import org.slf4j.ILoggerFactory;
import org.slf4j.Logger;
@@ -119,7 +127,7 @@ import static
org.apache.maven.shared.utils.logging.MessageUtils.buffer;
*
* @author Jason van Zyl
*/
-public class DaemonMavenCli {
+public class DaemonMavenCli implements DaemonCli {
public static final String LOCAL_REPO_PROPERTY = "maven.repo.local";
public static final String MULTIMODULE_PROJECT_DIRECTORY =
"maven.multiModuleProjectDirectory";
@@ -141,6 +149,8 @@ public class DaemonMavenCli {
public static final String STYLE_COLOR_PROPERTY = "style.color";
+ public static final String RESUME = "r";
+
public static final String RAW_STREAMS = "raw-streams";
private final Slf4jLoggerManager plexusLoggerManager;
@@ -169,9 +179,7 @@ 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 {
@@ -179,7 +187,8 @@ public class DaemonMavenCli {
slf4jLogger = slf4jLoggerFactory.getLogger(this.getClass().getName());
plexusLoggerManager = new Slf4jLoggerManager();
- this.classWorld = ((ClassRealm)
Thread.currentThread().getContextClassLoader()).getWorld();
+ ClassLoader cl = Thread.currentThread().getContextClassLoader();
+ classWorld = new ClassWorld("plexus.core", cl);
container = container();
@@ -303,6 +312,11 @@ public class DaemonMavenCli {
private CLIManager newCLIManager() {
CLIManager cliManager = new CLIManager();
+ cliManager.options.addOption(Option.builder(RESUME)
+ .longOpt("resume")
+ .desc("Resume reactor from "
+ + "the last failed project, using the
resume.properties file in the build directory")
+ .build());
cliManager.options.addOption(Option.builder()
.longOpt(RAW_STREAMS)
.desc("Do not decorate output and error streams")
@@ -345,12 +359,12 @@ public class DaemonMavenCli {
*/
void logging(CliRequest cliRequest) {
// LOG LEVEL
- 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);
+ 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);
ch.qos.logback.classic.Level level;
- if (cliRequest.verbose) {
+ if (cliRequest.debug) {
level = ch.qos.logback.classic.Level.DEBUG;
} else if (cliRequest.quiet) {
level = ch.qos.logback.classic.Level.WARN;
@@ -409,7 +423,7 @@ public class DaemonMavenCli {
}
private void version(CliRequest cliRequest) throws ExitException {
- if (cliRequest.verbose ||
cliRequest.commandLine.hasOption(CLIManager.VERSION)) {
+ if (cliRequest.debug ||
cliRequest.commandLine.hasOption(CLIManager.VERSION)) {
buildEventListener.log(CLIReportingUtils.showVersion());
if (cliRequest.commandLine.hasOption(CLIManager.VERSION)) {
throw new ExitException(0);
@@ -472,7 +486,6 @@ public class DaemonMavenCli {
List<File> extClassPath = Stream.of(
Environment.MVND_EXT_CLASSPATH.asString().split(","))
- .filter(s -> s != null && !s.isEmpty())
.map(File::new)
.collect(Collectors.toList());
@@ -519,11 +532,11 @@ public class DaemonMavenCli {
protected void configure() {
bind(ILoggerFactory.class).toInstance(slf4jLoggerFactory);
bind(CoreExports.class).toInstance(exports);
- bind(CoreExportsProvider.class).toInstance(new
CoreExportsProvider(exports));
bind(ExtensionRealmCache.class).to(InvalidatingExtensionRealmCache.class);
bind(PluginArtifactsCache.class).to(InvalidatingPluginArtifactsCache.class);
bind(PluginRealmCache.class).to(InvalidatingPluginRealmCache.class);
bind(ProjectArtifactsCache.class).to(InvalidatingProjectArtifactsCache.class);
+ bind(MavenPluginManager.class).to(CliMavenPluginManager.class);
bind(PluginVersionResolver.class).to(CachingPluginVersionResolver.class);
}
});
@@ -581,6 +594,7 @@ public class DaemonMavenCli {
populateRequest(
cliRequest,
cliRequest.request,
+ slf4jLogger,
eventSpyDispatcher,
container.lookup(ModelProcessor.class),
createTransferListener(cliRequest),
@@ -739,7 +753,22 @@ public class DaemonMavenCli {
}
}
- if (result.canResume()) {
+ 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) {
logBuildResumeHint("mvn <args> -r");
} else if (!failedProjects.isEmpty()) {
List<MavenProject> sortedProjects =
result.getTopologicallySortedProjects();
@@ -762,6 +791,8 @@ public class DaemonMavenCli {
return 1;
}
} else {
+ Path directory =
Paths.get(request.getBaseDirectory()).resolve("target");
+ new
DefaultBuildResumptionDataRepository().removeResumptionData(directory);
return 0;
}
}
@@ -1000,6 +1031,7 @@ public class DaemonMavenCli {
populateRequest(
cliRequest,
cliRequest.request,
+ slf4jLogger,
eventSpyDispatcher,
modelProcessor,
createTransferListener(cliRequest),
@@ -1007,9 +1039,10 @@ public class DaemonMavenCli {
executionListener);
}
- private void populateRequest(
+ private static void populateRequest(
CliRequest cliRequest,
MavenExecutionRequest request,
+ Logger slf4jLogger,
EventSpyDispatcher eventSpyDispatcher,
ModelProcessor modelProcessor,
TransferListener transferListener,
@@ -1017,283 +1050,244 @@ public class DaemonMavenCli {
LoggingExecutionListener executionListener) {
CommandLine commandLine = cliRequest.commandLine;
String workingDirectory = cliRequest.workingDirectory;
- boolean quiet = cliRequest.quiet;
- boolean verbose = cliRequest.verbose;
- request.setShowErrors(cliRequest.showErrors); // default: false
- File baseDirectory = new File(workingDirectory, "").getAbsoluteFile();
+ 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);
+ }
+ }
- 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);
+ //
----------------------------------------------------------------------
+ // Now that we have everything that we need we will fire up plexus and
+ // bring the maven component to life for use.
+ //
----------------------------------------------------------------------
- ExecutionEventLogger executionEventLogger = new ExecutionEventLogger();
-
executionListener.init(eventSpyDispatcher.chainListener(executionEventLogger),
buildEventListener);
+ if (commandLine.hasOption(CLIManager.BATCH_MODE)) {
+ request.setInteractiveMode(false);
+ }
- if ((request.getPom() != null) && (request.getPom().getParentFile() !=
null)) {
- request.setBaseDirectory(request.getPom().getParentFile());
+ boolean noSnapshotUpdates = false;
+ if (commandLine.hasOption(CLIManager.SUPRESS_SNAPSHOT_UPDATES)) {
+ noSnapshotUpdates = true;
}
-
request.setResumeFrom(commandLine.getOptionValue(CLIManager.RESUME_FROM));
- enableOnPresentOption(commandLine, CLIManager.RESUME,
request::setResume);
- request.setMakeBehavior(determineMakeBehavior(commandLine));
- request.setCacheNotFound(true);
- request.setCacheTransferError(false);
+ //
----------------------------------------------------------------------
+ //
+ //
----------------------------------------------------------------------
+
+ List<String> goals = commandLine.getArgList();
+
+ boolean recursive = true;
- performProjectActivation(commandLine, request.getProjectActivation());
- performProfileActivation(commandLine, request.getProfileActivation());
+ // this is the default behavior.
+ String reactorFailureBehaviour =
MavenExecutionRequest.REACTOR_FAIL_FAST;
- final String localRepositoryPath =
determineLocalRepositoryPath(request);
- if (localRepositoryPath != null) {
- request.setLocalRepositoryPath(localRepositoryPath);
+ if (commandLine.hasOption(CLIManager.NON_RECURSIVE)) {
+ recursive = false;
}
- //
- // 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.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 (threadConfiguration != null) {
- int degreeOfConcurrency =
calculateDegreeOfConcurrency(threadConfiguration);
- if (degreeOfConcurrency > 1) {
- request.setBuilderId("multithreaded");
- request.setDegreeOfConcurrency(degreeOfConcurrency);
- }
+ if (commandLine.hasOption(CLIManager.OFFLINE)) {
+ request.setOffline(true);
}
- //
- // Allow the builder to be overridden by the user if requested. The
builders are now pluggable.
- //
- request.setBuilderId(commandLine.getOptionValue(CLIManager.BUILDER,
request.getBuilderId()));
- }
+ boolean updateSnapshots = false;
- private String determineLocalRepositoryPath(final MavenExecutionRequest
request) {
- String userDefinedLocalRepo =
request.getUserProperties().getProperty(MavenCli.LOCAL_REPO_PROPERTY);
- if (userDefinedLocalRepo != null) {
- return userDefinedLocalRepo;
+ if (commandLine.hasOption(CLIManager.UPDATE_SNAPSHOTS)) {
+ updateSnapshots = true;
}
- return
request.getSystemProperties().getProperty(MavenCli.LOCAL_REPO_PROPERTY);
- }
+ String globalChecksumPolicy = null;
+
+ 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;
+ }
+
+ File baseDirectory = new File(workingDirectory, "").getAbsoluteFile();
+
+ //
----------------------------------------------------------------------
+ // Profile Activation
+ //
----------------------------------------------------------------------
+
+ List<String> activeProfiles = new ArrayList<>();
+
+ List<String> inactiveProfiles = new ArrayList<>();
+
+ 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);
+ }
+ }
+ }
+ }
+ }
+
+ ExecutionEventLogger executionEventLogger = new ExecutionEventLogger();
+
executionListener.init(eventSpyDispatcher.chainListener(executionEventLogger),
buildEventListener);
- 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");
}
- return pom;
+ request.setPom(pom);
} else if (modelProcessor != null) {
File pom = modelProcessor.locatePom(baseDirectory);
if (pom.isFile()) {
- return pom;
+ request.setPom(pom);
}
}
- return null;
- }
-
- // 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 (optionValues == null || optionValues.length == 0) {
- return;
- }
-
- 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);
- }
+ if ((request.getPom() != null) && (request.getPom().getParentFile() !=
null)) {
+ request.setBaseDirectory(request.getPom().getParentFile());
+ }
- boolean optional = selector.charAt(0) == '?';
- selector = selector.substring(optional ? 1 : 0);
+ if (commandLine.hasOption(RESUME)) {
+ new DefaultBuildResumptionDataRepository()
+ .applyResumptionData(
+ request,
Paths.get(request.getBaseDirectory()).resolve("target"));
+ }
- projectActivation.addProjectActivation(selector, active,
optional);
- }
- }
+ if (commandLine.hasOption(CLIManager.RESUME_FROM)) {
+
request.setResumeFrom(commandLine.getOptionValue(CLIManager.RESUME_FROM));
}
- }
- // 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 (commandLine.hasOption(CLIManager.PROJECT_LIST)) {
+ String[] projectOptionValues =
commandLine.getOptionValues(CLIManager.PROJECT_LIST);
- if (optionValues == null || optionValues.length == 0) {
- return;
- }
+ List<String> inclProjects = new ArrayList<>();
+ List<String> exclProjects = new ArrayList<>();
- 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);
- }
+ if (projectOptionValues != null) {
+ for (String projectOptionValue : projectOptionValues) {
+ StringTokenizer projectTokens = new
StringTokenizer(projectOptionValue, ",");
- boolean optional = profileId.charAt(0) == '?';
- profileId = profileId.substring(optional ? 1 : 0);
+ while (projectTokens.hasMoreTokens()) {
+ String projectAction =
projectTokens.nextToken().trim();
- profileActivation.addProfileActivation(profileId, active,
optional);
+ if (projectAction.startsWith("-") ||
projectAction.startsWith("!")) {
+ exclProjects.add(projectAction.substring(1));
+ } else if (projectAction.startsWith("+")) {
+ inclProjects.add(projectAction.substring(1));
+ } else {
+ inclProjects.add(projectAction);
+ }
+ }
}
}
- }
- }
-
- private ExecutionListener determineExecutionListener(EventSpyDispatcher
eventSpyDispatcher) {
- ExecutionListener executionListener = new ExecutionEventLogger();
- if (eventSpyDispatcher != null) {
- return eventSpyDispatcher.chainListener(executionListener);
- } else {
- return executionListener;
- }
- }
-
- 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;
- }
- }
- 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.setSelectedProjects(inclProjects);
+ request.setExcludedProjects(exclProjects);
}
- }
- 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;
+ 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 void disableOnPresentOption(
- final CommandLine commandLine, final String option, final
Consumer<Boolean> setting) {
- if (commandLine.hasOption(option)) {
- setting.accept(false);
- }
- }
+ String localRepoProperty =
request.getUserProperties().getProperty(MavenCli.LOCAL_REPO_PROPERTY);
- private void disableOnPresentOption(
- final CommandLine commandLine, final char option, final
Consumer<Boolean> setting) {
- disableOnPresentOption(commandLine, String.valueOf(option), setting);
- }
-
- private void enableOnPresentOption(
- final CommandLine commandLine, final String option, final
Consumer<Boolean> setting) {
- if (commandLine.hasOption(option)) {
- setting.accept(true);
+ if (localRepoProperty == null) {
+ localRepoProperty =
request.getSystemProperties().getProperty(MavenCli.LOCAL_REPO_PROPERTY);
}
- }
-
- 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);
+ if (localRepoProperty != null) {
+ request.setLocalRepositoryPath(localRepoProperty);
}
- }
-
- 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.");
- }
+ request.setCacheNotFound(true);
+ request.setCacheTransferError(false);
- float coreMultiplier = Float.parseFloat(threadConfiguration);
+ //
+ // 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;
- if (coreMultiplier <= 0.0f) {
- throw new IllegalArgumentException("Invalid threads core
multiplier value: '" + threadConfiguration
- + "C'. Value must be positive.");
- }
+ if (threadConfiguration != null) {
+ //
+ // Default to the standard multithreaded builder
+ //
+ request.setBuilderId("multithreaded");
- 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.");
+ if (threadConfiguration.contains("C")) {
+
request.setDegreeOfConcurrency(calculateDegreeOfConcurrencyWithCoreMultiplier(threadConfiguration));
+ } else {
+
request.setDegreeOfConcurrency(Integer.parseInt(threadConfiguration));
}
+ }
- 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.");
- }
+ //
+ // 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));
}
}
+ static int calculateDegreeOfConcurrencyWithCoreMultiplier(String
threadConfiguration) {
+ int procs = Runtime.getRuntime().availableProcessors();
+ return (int) (Float.parseFloat(threadConfiguration.replace("C", "")) *
procs);
+ }
+
static File resolveFile(File file, String workingDirectory) {
if (file == null) {
return null;
@@ -1320,9 +1314,15 @@ public class DaemonMavenCli {
// are most dominant.
//
----------------------------------------------------------------------
- final Properties userSpecifiedProperties =
-
commandLine.getOptionProperties(String.valueOf(CLIManager.SET_SYSTEM_PROPERTY));
- userSpecifiedProperties.forEach((prop, value) ->
setCliProperty((String) prop, (String) value, userProperties));
+ 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);
+ }
+ }
+ }
SystemProperties.addSystemProperties(systemProperties);
@@ -1351,7 +1351,23 @@ public class DaemonMavenCli {
}
}
- private static void setCliProperty(String name, String value, Properties
properties) {
+ 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);
+ }
+
properties.setProperty(name, value);
//
----------------------------------------------------------------------
diff --git
a/daemon/src/main/java/org/apache/maven/project/SnapshotModelCache.java
b/daemon-m39/src/main/java/org/apache/maven/project/SnapshotModelCache.java
similarity index 83%
copy from daemon/src/main/java/org/apache/maven/project/SnapshotModelCache.java
copy to
daemon-m39/src/main/java/org/apache/maven/project/SnapshotModelCache.java
index 39ff8b04..8d7d20ea 100644
--- a/daemon/src/main/java/org/apache/maven/project/SnapshotModelCache.java
+++ b/daemon-m39/src/main/java/org/apache/maven/project/SnapshotModelCache.java
@@ -18,7 +18,8 @@
*/
package org.apache.maven.project;
-import org.apache.maven.building.Source;
+import java.util.Objects;
+
import org.apache.maven.model.building.ModelCache;
public class SnapshotModelCache implements ModelCache {
@@ -27,16 +28,8 @@ public class SnapshotModelCache implements ModelCache {
private final ModelCache reactorCache;
public SnapshotModelCache(ModelCache globalCache, ModelCache reactorCache)
{
- this.globalCache = globalCache;
- this.reactorCache = reactorCache;
- }
-
- public Object get(Source path, String tag) {
- return reactorCache.get(path, tag);
- }
-
- public void put(Source path, String tag, Object data) {
- reactorCache.put(path, tag, data);
+ this.globalCache = Objects.requireNonNull(globalCache);
+ this.reactorCache = Objects.requireNonNull(reactorCache);
}
@Override
diff --git
a/daemon/src/main/java/org/apache/maven/project/SnapshotModelCacheFactory.java
b/daemon-m39/src/main/java/org/apache/maven/project/SnapshotModelCacheFactory.java
similarity index 76%
copy from
daemon/src/main/java/org/apache/maven/project/SnapshotModelCacheFactory.java
copy to
daemon-m39/src/main/java/org/apache/maven/project/SnapshotModelCacheFactory.java
index fe285af6..08946257 100644
---
a/daemon/src/main/java/org/apache/maven/project/SnapshotModelCacheFactory.java
+++
b/daemon-m39/src/main/java/org/apache/maven/project/SnapshotModelCacheFactory.java
@@ -25,12 +25,11 @@ import javax.inject.Singleton;
import org.apache.maven.model.building.ModelCache;
import org.apache.maven.repository.internal.DefaultModelCacheFactory;
import org.apache.maven.repository.internal.ModelCacheFactory;
+import org.eclipse.aether.DefaultRepositoryCache;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.sisu.Priority;
-import static org.mvndaemon.mvnd.common.Environment.MVND_NO_MODEL_CACHE;
-
@Singleton
@Named
@Priority(10)
@@ -42,15 +41,13 @@ public class SnapshotModelCacheFactory implements
ModelCacheFactory {
@Inject
public SnapshotModelCacheFactory(DefaultModelCacheFactory factory) {
this.factory = factory;
- this.globalCache = factory.createCache(new
DefaultRepositorySystemSession());
+ DefaultRepositorySystemSession session = new
DefaultRepositorySystemSession();
+ session.setCache(new DefaultRepositoryCache());
+ this.globalCache = factory.createCache(session);
}
@Override
public ModelCache createCache(RepositorySystemSession session) {
- boolean noModelCache =
-
Boolean.parseBoolean(MVND_NO_MODEL_CACHE.asOptional().orElse(MVND_NO_MODEL_CACHE.getDefault()));
- ModelCache reactorCache = factory.createCache(session);
- ModelCache globalCache = noModelCache ? reactorCache :
this.globalCache;
- return new SnapshotModelCache(globalCache, reactorCache);
+ return new SnapshotModelCache(globalCache,
factory.createCache(session));
}
}
diff --git
a/daemon/src/main/java/org/mvndaemon/mvnd/cache/invalidating/InvalidatingPluginDescriptorCache.java
b/daemon-m39/src/main/java/org/mvndaemon/mvnd/cache/invalidating/InvalidatingPluginDescriptorCache.java
similarity index 100%
copy from
daemon/src/main/java/org/mvndaemon/mvnd/cache/invalidating/InvalidatingPluginDescriptorCache.java
copy to
daemon-m39/src/main/java/org/mvndaemon/mvnd/cache/invalidating/InvalidatingPluginDescriptorCache.java
diff --git
a/daemon/src/main/java/org/mvndaemon/mvnd/cache/invalidating/InvalidatingPluginRealmCache.java
b/daemon-m39/src/main/java/org/mvndaemon/mvnd/cache/invalidating/InvalidatingPluginRealmCache.java
similarity index 96%
copy from
daemon/src/main/java/org/mvndaemon/mvnd/cache/invalidating/InvalidatingPluginRealmCache.java
copy to
daemon-m39/src/main/java/org/mvndaemon/mvnd/cache/invalidating/InvalidatingPluginRealmCache.java
index 805866e5..67796309 100644
---
a/daemon/src/main/java/org/mvndaemon/mvnd/cache/invalidating/InvalidatingPluginRealmCache.java
+++
b/daemon-m39/src/main/java/org/mvndaemon/mvnd/cache/invalidating/InvalidatingPluginRealmCache.java
@@ -42,6 +42,11 @@ import org.mvndaemon.mvnd.cache.CacheFactory;
@Priority(10)
public class InvalidatingPluginRealmCache extends DefaultPluginRealmCache {
+ @FunctionalInterface
+ public interface PluginRealmSupplier {
+ CacheRecord load() throws PluginResolutionException,
PluginContainerException;
+ }
+
protected static class Record implements
org.mvndaemon.mvnd.cache.CacheRecord {
final CacheRecord record;
@@ -80,7 +85,6 @@ public class InvalidatingPluginRealmCache extends
DefaultPluginRealmCache {
return r != null ? r.record : null;
}
- @Override
public CacheRecord get(Key key, PluginRealmSupplier supplier)
throws PluginResolutionException, PluginContainerException {
try {
diff --git
a/daemon/src/main/java/org/mvndaemon/mvnd/cache/invalidating/InvalidatingRealmCacheEventSpy.java
b/daemon-m39/src/main/java/org/mvndaemon/mvnd/cache/invalidating/InvalidatingRealmCacheEventSpy.java
similarity index 100%
copy from
daemon/src/main/java/org/mvndaemon/mvnd/cache/invalidating/InvalidatingRealmCacheEventSpy.java
copy to
daemon-m39/src/main/java/org/mvndaemon/mvnd/cache/invalidating/InvalidatingRealmCacheEventSpy.java
diff --git
a/daemon-m39/src/main/java/org/mvndaemon/mvnd/execution/BuildResumptionAnalyzer.java
b/daemon-m39/src/main/java/org/mvndaemon/mvnd/execution/BuildResumptionAnalyzer.java
new file mode 100644
index 00000000..c0c0ac26
--- /dev/null
+++
b/daemon-m39/src/main/java/org/mvndaemon/mvnd/execution/BuildResumptionAnalyzer.java
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+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-m39/src/main/java/org/mvndaemon/mvnd/execution/BuildResumptionData.java
b/daemon-m39/src/main/java/org/mvndaemon/mvnd/execution/BuildResumptionData.java
new file mode 100644
index 00000000..b9ae3eda
--- /dev/null
+++
b/daemon-m39/src/main/java/org/mvndaemon/mvnd/execution/BuildResumptionData.java
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+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-m39/src/main/java/org/mvndaemon/mvnd/execution/BuildResumptionDataRepository.java
b/daemon-m39/src/main/java/org/mvndaemon/mvnd/execution/BuildResumptionDataRepository.java
new file mode 100644
index 00000000..6dbae8b8
--- /dev/null
+++
b/daemon-m39/src/main/java/org/mvndaemon/mvnd/execution/BuildResumptionDataRepository.java
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+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-m39/src/main/java/org/mvndaemon/mvnd/execution/BuildResumptionPersistenceException.java
b/daemon-m39/src/main/java/org/mvndaemon/mvnd/execution/BuildResumptionPersistenceException.java
new file mode 100644
index 00000000..00f99ca3
--- /dev/null
+++
b/daemon-m39/src/main/java/org/mvndaemon/mvnd/execution/BuildResumptionPersistenceException.java
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+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-m39/src/main/java/org/mvndaemon/mvnd/execution/DefaultBuildResumptionAnalyzer.java
b/daemon-m39/src/main/java/org/mvndaemon/mvnd/execution/DefaultBuildResumptionAnalyzer.java
new file mode 100644
index 00000000..96da4dc0
--- /dev/null
+++
b/daemon-m39/src/main/java/org/mvndaemon/mvnd/execution/DefaultBuildResumptionAnalyzer.java
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ */
+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 javax.inject.Named;
+import javax.inject.Singleton;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+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-m39/src/main/java/org/mvndaemon/mvnd/execution/DefaultBuildResumptionDataRepository.java
b/daemon-m39/src/main/java/org/mvndaemon/mvnd/execution/DefaultBuildResumptionDataRepository.java
new file mode 100644
index 00000000..d61c5151
--- /dev/null
+++
b/daemon-m39/src/main/java/org/mvndaemon/mvnd/execution/DefaultBuildResumptionDataRepository.java
@@ -0,0 +1,155 @@
+/*
+ * 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.
+ */
+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 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.Properties;
+import java.util.stream.Stream;
+
+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-m39/src/main/java/org/mvndaemon/mvnd/plugin/CachingPluginVersionResolver.java
similarity index 84%
copy from
daemon/src/main/java/org/mvndaemon/mvnd/plugin/CachingPluginVersionResolver.java
copy to
daemon-m39/src/main/java/org/mvndaemon/mvnd/plugin/CachingPluginVersionResolver.java
index 13745e8a..19c73f9b 100644
---
a/daemon/src/main/java/org/mvndaemon/mvnd/plugin/CachingPluginVersionResolver.java
+++
b/daemon-m39/src/main/java/org/mvndaemon/mvnd/plugin/CachingPluginVersionResolver.java
@@ -18,7 +18,6 @@
*/
package org.mvndaemon.mvnd.plugin;
-import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
@@ -27,17 +26,13 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;
-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;
@@ -49,15 +44,6 @@ 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 =
diff --git
a/daemon-m39/src/main/java/org/mvndaemon/mvnd/plugin/CliMavenPluginManager.java
b/daemon-m39/src/main/java/org/mvndaemon/mvnd/plugin/CliMavenPluginManager.java
new file mode 100644
index 00000000..5edb9d64
--- /dev/null
+++
b/daemon-m39/src/main/java/org/mvndaemon/mvnd/plugin/CliMavenPluginManager.java
@@ -0,0 +1,779 @@
+/*
+ * 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.
+ */
+package org.mvndaemon.mvnd.plugin;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import java.io.*;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.*;
+import java.util.jar.JarFile;
+import java.util.zip.ZipEntry;
+
+import org.apache.maven.RepositoryUtils;
+import org.apache.maven.artifact.Artifact;
+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.*;
+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.PluginDependenciesResolver;
+import org.apache.maven.plugin.version.DefaultPluginVersionRequest;
+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.project.ExtensionDescriptor;
+import org.apache.maven.project.ExtensionDescriptorBuilder;
+import org.apache.maven.project.MavenProject;
+import org.apache.maven.rtinfo.RuntimeInformation;
+import org.apache.maven.session.scope.internal.SessionScopeModule;
+import org.codehaus.plexus.DefaultPlexusContainer;
+import org.codehaus.plexus.PlexusContainer;
+import org.codehaus.plexus.classworlds.realm.ClassRealm;
+import
org.codehaus.plexus.component.composition.CycleDetectedInComponentGraphException;
+import
org.codehaus.plexus.component.configurator.ComponentConfigurationException;
+import org.codehaus.plexus.component.configurator.ComponentConfigurator;
+import org.codehaus.plexus.component.configurator.ConfigurationListener;
+import
org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
+import
org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator;
+import org.codehaus.plexus.component.repository.ComponentDescriptor;
+import
org.codehaus.plexus.component.repository.exception.ComponentLifecycleException;
+import
org.codehaus.plexus.component.repository.exception.ComponentLookupException;
+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;
+import org.codehaus.plexus.util.xml.Xpp3Dom;
+import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.graph.DependencyFilter;
+import org.eclipse.aether.graph.DependencyNode;
+import org.eclipse.aether.repository.RemoteRepository;
+import org.eclipse.aether.util.filter.AndDependencyFilter;
+import org.eclipse.aether.util.graph.visitor.PreorderNodeListGenerator;
+import org.eclipse.sisu.Priority;
+import org.eclipse.sisu.Typed;
+import org.mvndaemon.mvnd.cache.invalidating.InvalidatingPluginDescriptorCache;
+import org.mvndaemon.mvnd.cache.invalidating.InvalidatingPluginRealmCache;
+
+/*
+ * gnodet: This file is based on maven DefaultMavenPluginManager and changed
in order
+ * to better support parallel builds. See
https://github.com/apache/maven-mvnd/issues/310
+ */
+/**
+ * Provides basic services to manage Maven plugins and their mojos. This
component is kept general in its design such
+ * that the plugins/mojos can be used in arbitrary contexts. In particular,
the mojos can be used for ordinary build
+ * plugins as well as special purpose plugins like reports.
+ *
+ * @author Benjamin Bentmann
+ * @since 3.0
+ */
+@Singleton
+@Named
+@Priority(10)
+@Typed(MavenPluginManager.class)
+public class CliMavenPluginManager implements MavenPluginManager {
+
+ /**
+ * <p>
+ * PluginId => ExtensionRealmCache.CacheRecord map MavenProject context
value key. The map is used to ensure the
+ * same class realm is used to load build extensions and load mojos for
extensions=true plugins.
+ * </p>
+ * <strong>Note:</strong> This is part of internal implementation and may
be changed or removed without notice
+ *
+ * @since 3.3.0
+ */
+ public static final String KEY_EXTENSIONS_REALMS =
CliMavenPluginManager.class.getName() + "/extensionsRealms";
+
+ @Inject
+ private Logger logger;
+
+ @Inject
+ private LoggerManager loggerManager;
+
+ @Inject
+ private PlexusContainer container;
+
+ @Inject
+ private ClassRealmManager classRealmManager;
+
+ @Inject
+ private InvalidatingPluginDescriptorCache pluginDescriptorCache;
+
+ @Inject
+ private InvalidatingPluginRealmCache pluginRealmCache;
+
+ @Inject
+ private PluginDependenciesResolver pluginDependenciesResolver;
+
+ @Inject
+ private RuntimeInformation runtimeInformation;
+
+ @Inject
+ private ExtensionRealmCache extensionRealmCache;
+
+ @Inject
+ private PluginVersionResolver pluginVersionResolver;
+
+ @Inject
+ private PluginArtifactsCache pluginArtifactsCache;
+
+ private ExtensionDescriptorBuilder extensionDescriptorBuilder = new
ExtensionDescriptorBuilder();
+
+ private PluginDescriptorBuilder builder = new PluginDescriptorBuilder();
+
+ public PluginDescriptor getPluginDescriptor(
+ Plugin plugin, List<RemoteRepository> repositories,
RepositorySystemSession session)
+ throws PluginResolutionException,
PluginDescriptorParsingException, InvalidPluginDescriptorException {
+ PluginDescriptorCache.Key cacheKey =
pluginDescriptorCache.createKey(plugin, repositories, session);
+
+ PluginDescriptor pluginDescriptor =
pluginDescriptorCache.get(cacheKey, () -> {
+ org.eclipse.aether.artifact.Artifact artifact =
+ pluginDependenciesResolver.resolve(plugin, repositories,
session);
+
+ Artifact pluginArtifact = RepositoryUtils.toArtifact(artifact);
+
+ PluginDescriptor descriptor =
extractPluginDescriptor(pluginArtifact, plugin);
+
+
descriptor.setRequiredMavenVersion(artifact.getProperty("requiredMavenVersion",
null));
+
+ return descriptor;
+ });
+
+ pluginDescriptor.setPlugin(plugin);
+
+ return pluginDescriptor;
+ }
+
+ private PluginDescriptor extractPluginDescriptor(Artifact pluginArtifact,
Plugin plugin)
+ throws PluginDescriptorParsingException,
InvalidPluginDescriptorException {
+ PluginDescriptor pluginDescriptor = null;
+
+ File pluginFile = pluginArtifact.getFile();
+
+ try {
+ if (pluginFile.isFile()) {
+ try (JarFile pluginJar = new JarFile(pluginFile, false)) {
+ ZipEntry pluginDescriptorEntry =
pluginJar.getEntry(getPluginDescriptorLocation());
+
+ if (pluginDescriptorEntry != null) {
+ InputStream is =
pluginJar.getInputStream(pluginDescriptorEntry);
+
+ pluginDescriptor = parsePluginDescriptor(is, plugin,
pluginFile.getAbsolutePath());
+ }
+ }
+ } else {
+ File pluginXml = new File(pluginFile,
getPluginDescriptorLocation());
+
+ if (pluginXml.isFile()) {
+ try (InputStream is = new BufferedInputStream(new
FileInputStream(pluginXml))) {
+ pluginDescriptor = parsePluginDescriptor(is, plugin,
pluginXml.getAbsolutePath());
+ }
+ }
+ }
+
+ if (pluginDescriptor == null) {
+ throw new IOException("No plugin descriptor found at " +
getPluginDescriptorLocation());
+ }
+ } catch (IOException e) {
+ throw new PluginDescriptorParsingException(plugin,
pluginFile.getAbsolutePath(), e);
+ }
+
+ MavenPluginValidator validator = new
MavenPluginValidator(pluginArtifact);
+
+ validator.validate(pluginDescriptor);
+
+ if (validator.hasErrors()) {
+ throw new InvalidPluginDescriptorException(
+ "Invalid plugin descriptor for " + plugin.getId() + " (" +
pluginFile + ")", validator.getErrors());
+ }
+
+ pluginDescriptor.setPluginArtifact(pluginArtifact);
+
+ return pluginDescriptor;
+ }
+
+ private String getPluginDescriptorLocation() {
+ return "META-INF/maven/plugin.xml";
+ }
+
+ private PluginDescriptor parsePluginDescriptor(InputStream is, Plugin
plugin, String descriptorLocation)
+ throws PluginDescriptorParsingException {
+ try {
+ Reader reader = ReaderFactory.newXmlReader(is);
+
+ PluginDescriptor pluginDescriptor = builder.build(reader,
descriptorLocation);
+
+ return pluginDescriptor;
+ } catch (IOException | PlexusConfigurationException e) {
+ throw new PluginDescriptorParsingException(plugin,
descriptorLocation, e);
+ }
+ }
+
+ public MojoDescriptor getMojoDescriptor(
+ Plugin plugin, String goal, List<RemoteRepository> repositories,
RepositorySystemSession session)
+ throws MojoNotFoundException, PluginResolutionException,
PluginDescriptorParsingException,
+ InvalidPluginDescriptorException {
+ PluginDescriptor pluginDescriptor = getPluginDescriptor(plugin,
repositories, session);
+
+ MojoDescriptor mojoDescriptor = pluginDescriptor.getMojo(goal);
+
+ if (mojoDescriptor == null) {
+ throw new MojoNotFoundException(goal, pluginDescriptor);
+ }
+
+ return mojoDescriptor;
+ }
+
+ public void checkRequiredMavenVersion(PluginDescriptor pluginDescriptor)
throws PluginIncompatibleException {
+ String requiredMavenVersion =
pluginDescriptor.getRequiredMavenVersion();
+ if (StringUtils.isNotBlank(requiredMavenVersion)) {
+ try {
+ if (!runtimeInformation.isMavenVersion(requiredMavenVersion)) {
+ throw new PluginIncompatibleException(
+ pluginDescriptor.getPlugin(),
+ "The plugin " + pluginDescriptor.getId() + "
requires Maven version "
+ + requiredMavenVersion);
+ }
+ } catch (RuntimeException e) {
+ logger.warn("Could not verify plugin's Maven prerequisite: " +
e.getMessage());
+ }
+ }
+ }
+
+ public void setupPluginRealm(
+ PluginDescriptor pluginDescriptor,
+ MavenSession session,
+ ClassLoader parent,
+ List<String> imports,
+ DependencyFilter filter)
+ throws PluginResolutionException, PluginContainerException {
+ Plugin plugin = pluginDescriptor.getPlugin();
+ MavenProject project = session.getCurrentProject();
+
+ if (plugin.isExtensions()) {
+ ExtensionRealmCache.CacheRecord extensionRecord;
+ try {
+ RepositorySystemSession repositorySession =
session.getRepositorySession();
+ extensionRecord = setupExtensionsRealm(project, plugin,
repositorySession);
+ } catch (PluginManagerException e) {
+ // extensions realm is expected to be fully setup at this point
+ // any exception means a problem in maven code, not a user
error
+ throw new IllegalStateException(e);
+ }
+
+ ClassRealm pluginRealm = extensionRecord.getRealm();
+ List<Artifact> pluginArtifacts = extensionRecord.getArtifacts();
+
+ for (ComponentDescriptor<?> componentDescriptor :
pluginDescriptor.getComponents()) {
+ componentDescriptor.setRealm(pluginRealm);
+ }
+
+ pluginDescriptor.setClassRealm(pluginRealm);
+ pluginDescriptor.setArtifacts(pluginArtifacts);
+ } else {
+ Map<String, ClassLoader> foreignImports = calcImports(project,
parent, imports);
+
+ PluginRealmCache.Key cacheKey = pluginRealmCache.createKey(
+ plugin,
+ parent,
+ foreignImports,
+ filter,
+ project.getRemotePluginRepositories(),
+ session.getRepositorySession());
+
+ PluginRealmCache.CacheRecord cacheRecord =
pluginRealmCache.get(cacheKey, () -> {
+ createPluginRealm(pluginDescriptor, session, parent,
foreignImports, filter);
+ return new PluginRealmCache.CacheRecord(
+ pluginDescriptor.getClassRealm(),
pluginDescriptor.getArtifacts());
+ });
+
+ if (cacheRecord != null) {
+ pluginDescriptor.setClassRealm(cacheRecord.getRealm());
+ pluginDescriptor.setArtifacts(new
ArrayList<>(cacheRecord.getArtifacts()));
+ for (ComponentDescriptor<?> componentDescriptor :
pluginDescriptor.getComponents()) {
+ componentDescriptor.setRealm(cacheRecord.getRealm());
+ }
+ }
+
+ pluginRealmCache.register(project, cacheKey, cacheRecord);
+ }
+ }
+
+ private void createPluginRealm(
+ PluginDescriptor pluginDescriptor,
+ MavenSession session,
+ ClassLoader parent,
+ Map<String, ClassLoader> foreignImports,
+ DependencyFilter filter)
+ throws PluginResolutionException, PluginContainerException {
+ Plugin plugin = Objects.requireNonNull(pluginDescriptor.getPlugin(),
"pluginDescriptor.plugin cannot be null");
+
+ Artifact pluginArtifact = Objects.requireNonNull(
+ pluginDescriptor.getPluginArtifact(),
"pluginDescriptor.pluginArtifact cannot be null");
+
+ MavenProject project = session.getCurrentProject();
+
+ final ClassRealm pluginRealm;
+ final List<Artifact> pluginArtifacts;
+
+ RepositorySystemSession repositorySession =
session.getRepositorySession();
+ DependencyFilter dependencyFilter =
project.getExtensionDependencyFilter();
+ dependencyFilter = AndDependencyFilter.newInstance(dependencyFilter,
filter);
+
+ DependencyNode root = pluginDependenciesResolver.resolve(
+ plugin,
+ RepositoryUtils.toArtifact(pluginArtifact),
+ dependencyFilter,
+ project.getRemotePluginRepositories(),
+ repositorySession);
+
+ PreorderNodeListGenerator nlg = new PreorderNodeListGenerator();
+ root.accept(nlg);
+
+ pluginArtifacts = toMavenArtifacts(root, nlg);
+
+ if (parent == null) {
+ parent = new URLClassLoader(new URL[0]);
+ }
+ pluginRealm = classRealmManager.createPluginRealm(
+ plugin, parent, null, foreignImports,
toAetherArtifacts(pluginArtifacts));
+
+ discoverPluginComponents(pluginRealm, plugin, pluginDescriptor);
+
+ pluginDescriptor.setClassRealm(pluginRealm);
+ pluginDescriptor.setArtifacts(pluginArtifacts);
+ }
+
+ private void discoverPluginComponents(
+ final ClassRealm pluginRealm, Plugin plugin, PluginDescriptor
pluginDescriptor)
+ throws PluginContainerException {
+ try {
+ if (pluginDescriptor != null) {
+ for (ComponentDescriptor<?> componentDescriptor :
pluginDescriptor.getComponents()) {
+ componentDescriptor.setRealm(pluginRealm);
+ container.addComponentDescriptor(componentDescriptor);
+ }
+ }
+
+ ((DefaultPlexusContainer) container)
+ .discoverComponents(
+ pluginRealm, new SessionScopeModule(container),
new MojoExecutionScopeModule(container));
+ } catch (ComponentLookupException |
CycleDetectedInComponentGraphException e) {
+ throw new PluginContainerException(
+ plugin,
+ pluginRealm,
+ "Error in component graph of plugin " + plugin.getId() +
": " + e.getMessage(),
+ e);
+ }
+ }
+
+ private List<org.eclipse.aether.artifact.Artifact> toAetherArtifacts(final
List<Artifact> pluginArtifacts) {
+ return new ArrayList<>(RepositoryUtils.toArtifacts(pluginArtifacts));
+ }
+
+ private List<Artifact> toMavenArtifacts(DependencyNode root,
PreorderNodeListGenerator nlg) {
+ List<Artifact> artifacts = new ArrayList<>(nlg.getNodes().size());
+ RepositoryUtils.toArtifacts(artifacts, Collections.singleton(root),
Collections.<String>emptyList(), null);
+ for (Iterator<Artifact> it = artifacts.iterator(); it.hasNext(); ) {
+ Artifact artifact = it.next();
+ if (artifact.getFile() == null) {
+ it.remove();
+ }
+ }
+ return Collections.unmodifiableList(artifacts);
+ }
+
+ private Map<String, ClassLoader> calcImports(MavenProject project,
ClassLoader parent, List<String> imports) {
+ Map<String, ClassLoader> foreignImports = new HashMap<>();
+
+ ClassLoader projectRealm = project.getClassRealm();
+ if (projectRealm != null) {
+ foreignImports.put("", projectRealm);
+ } else {
+ foreignImports.put("", classRealmManager.getMavenApiRealm());
+ }
+
+ if (parent != null && imports != null) {
+ for (String parentImport : imports) {
+ foreignImports.put(parentImport, parent);
+ }
+ }
+
+ return foreignImports;
+ }
+
+ public <T> T getConfiguredMojo(Class<T> mojoInterface, MavenSession
session, MojoExecution mojoExecution)
+ throws PluginConfigurationException, PluginContainerException {
+ MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor();
+
+ PluginDescriptor pluginDescriptor =
mojoDescriptor.getPluginDescriptor();
+
+ ClassRealm pluginRealm = pluginDescriptor.getClassRealm();
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("Configuring mojo " + mojoDescriptor.getId() + " from
plugin realm " + pluginRealm);
+ }
+
+ // We are forcing the use of the plugin realm for all lookups that
might occur during
+ // the lifecycle that is part of the lookup. Here we are specifically
trying to keep
+ // lookups that occur in contextualize calls in line with the right
realm.
+ ClassRealm oldLookupRealm = container.setLookupRealm(pluginRealm);
+
+ ClassLoader oldClassLoader =
Thread.currentThread().getContextClassLoader();
+ Thread.currentThread().setContextClassLoader(pluginRealm);
+
+ try {
+ T mojo;
+
+ try {
+ mojo = container.lookup(mojoInterface,
mojoDescriptor.getRoleHint());
+ } catch (ComponentLookupException e) {
+ Throwable cause = e.getCause();
+ while (cause != null
+ && !(cause instanceof LinkageError)
+ && !(cause instanceof ClassNotFoundException)) {
+ cause = cause.getCause();
+ }
+
+ if ((cause instanceof NoClassDefFoundError) || (cause
instanceof ClassNotFoundException)) {
+ ByteArrayOutputStream os = new ByteArrayOutputStream(1024);
+ PrintStream ps = new PrintStream(os);
+ ps.println("Unable to load the mojo '" +
mojoDescriptor.getGoal() + "' in the plugin '"
+ + pluginDescriptor.getId() + "'. A required class
is missing: "
+ + cause.getMessage());
+ pluginRealm.display(ps);
+
+ throw new PluginContainerException(mojoDescriptor,
pluginRealm, os.toString(), cause);
+ } else if (cause instanceof LinkageError) {
+ ByteArrayOutputStream os = new ByteArrayOutputStream(1024);
+ PrintStream ps = new PrintStream(os);
+ ps.println("Unable to load the mojo '" +
mojoDescriptor.getGoal() + "' in the plugin '"
+ + pluginDescriptor.getId() + "' due to an API
incompatibility: "
+ + e.getClass().getName() + ": " +
cause.getMessage());
+ pluginRealm.display(ps);
+
+ throw new PluginContainerException(mojoDescriptor,
pluginRealm, os.toString(), cause);
+ }
+
+ throw new PluginContainerException(
+ mojoDescriptor,
+ pluginRealm,
+ "Unable to load the mojo '" + mojoDescriptor.getGoal()
+ + "' (or one of its required components) from
the plugin '"
+ + pluginDescriptor.getId() + "'",
+ e);
+ }
+
+ if (mojo instanceof ContextEnabled) {
+ MavenProject project = session.getCurrentProject();
+
+ Map<String, Object> pluginContext =
session.getPluginContext(pluginDescriptor, project);
+
+ if (pluginContext != null) {
+ pluginContext.put("project", project);
+
+ pluginContext.put("pluginDescriptor", pluginDescriptor);
+
+ ((ContextEnabled) mojo).setPluginContext(pluginContext);
+ }
+ }
+
+ if (mojo instanceof Mojo) {
+ Logger mojoLogger =
loggerManager.getLoggerForComponent(mojoDescriptor.getImplementation());
+ ((Mojo) mojo).setLog(new DefaultLog(mojoLogger));
+ }
+
+ Xpp3Dom dom = mojoExecution.getConfiguration();
+
+ PlexusConfiguration pomConfiguration;
+
+ if (dom == null) {
+ pomConfiguration = new XmlPlexusConfiguration("configuration");
+ } else {
+ pomConfiguration = new XmlPlexusConfiguration(dom);
+ }
+
+ ExpressionEvaluator expressionEvaluator = new
PluginParameterExpressionEvaluator(session, mojoExecution);
+
+ populatePluginFields(mojo, mojoDescriptor, pluginRealm,
pomConfiguration, expressionEvaluator);
+
+ return mojo;
+ } finally {
+ Thread.currentThread().setContextClassLoader(oldClassLoader);
+ container.setLookupRealm(oldLookupRealm);
+ }
+ }
+
+ private void populatePluginFields(
+ Object mojo,
+ MojoDescriptor mojoDescriptor,
+ ClassRealm pluginRealm,
+ PlexusConfiguration configuration,
+ ExpressionEvaluator expressionEvaluator)
+ throws PluginConfigurationException {
+ ComponentConfigurator configurator = null;
+
+ String configuratorId = mojoDescriptor.getComponentConfigurator();
+
+ if (StringUtils.isEmpty(configuratorId)) {
+ configuratorId = "basic";
+ }
+
+ try {
+ // TODO could the configuration be passed to lookup and the
configurator known to plexus via the descriptor
+ // so that this method could entirely be handled by a plexus
lookup?
+ configurator = container.lookup(ComponentConfigurator.class,
configuratorId);
+
+ ConfigurationListener listener = new
DebugConfigurationListener(logger);
+
+ ValidatingConfigurationListener validator =
+ new ValidatingConfigurationListener(mojo, mojoDescriptor,
listener);
+
+ logger.debug(
+ "Configuring mojo '" + mojoDescriptor.getId() + "' with "
+ configuratorId + " configurator -->");
+
+ configurator.configureComponent(mojo, configuration,
expressionEvaluator, pluginRealm, validator);
+
+ logger.debug("-- end configuration --");
+
+ Collection<Parameter> missingParameters =
validator.getMissingParameters();
+ if (!missingParameters.isEmpty()) {
+ if ("basic".equals(configuratorId)) {
+ throw new PluginParameterException(mojoDescriptor, new
ArrayList<>(missingParameters));
+ } else {
+ /*
+ * NOTE: Other configurators like the map-oriented one
don't call into the listener, so do it the
+ * hard way.
+ */
+ validateParameters(mojoDescriptor, configuration,
expressionEvaluator);
+ }
+ }
+ } catch (ComponentConfigurationException e) {
+ String message = "Unable to parse configuration of mojo " +
mojoDescriptor.getId();
+ if (e.getFailedConfiguration() != null) {
+ message += " for parameter " +
e.getFailedConfiguration().getName();
+ }
+ message += ": " + e.getMessage();
+
+ throw new
PluginConfigurationException(mojoDescriptor.getPluginDescriptor(), message, e);
+ } catch (ComponentLookupException e) {
+ throw new PluginConfigurationException(
+ mojoDescriptor.getPluginDescriptor(),
+ "Unable to retrieve component configurator " +
configuratorId + " for configuration of mojo "
+ + mojoDescriptor.getId(),
+ e);
+ } catch (NoClassDefFoundError e) {
+ ByteArrayOutputStream os = new ByteArrayOutputStream(1024);
+ PrintStream ps = new PrintStream(os);
+ ps.println("A required class was missing during configuration of
mojo " + mojoDescriptor.getId() + ": "
+ + e.getMessage());
+ pluginRealm.display(ps);
+
+ throw new
PluginConfigurationException(mojoDescriptor.getPluginDescriptor(),
os.toString(), e);
+ } catch (LinkageError e) {
+ ByteArrayOutputStream os = new ByteArrayOutputStream(1024);
+ PrintStream ps = new PrintStream(os);
+ ps.println("An API incompatibility was encountered during
configuration of mojo " + mojoDescriptor.getId()
+ + ": " + e.getClass().getName() + ": " + e.getMessage());
+ pluginRealm.display(ps);
+
+ throw new
PluginConfigurationException(mojoDescriptor.getPluginDescriptor(),
os.toString(), e);
+ } finally {
+ if (configurator != null) {
+ try {
+ container.release(configurator);
+ } catch (ComponentLifecycleException e) {
+ logger.debug("Failed to release mojo configurator -
ignoring.");
+ }
+ }
+ }
+ }
+
+ private void validateParameters(
+ MojoDescriptor mojoDescriptor, PlexusConfiguration configuration,
ExpressionEvaluator expressionEvaluator)
+ throws ComponentConfigurationException, PluginParameterException {
+ if (mojoDescriptor.getParameters() == null) {
+ return;
+ }
+
+ List<Parameter> invalidParameters = new ArrayList<>();
+
+ for (Parameter parameter : mojoDescriptor.getParameters()) {
+ if (!parameter.isRequired()) {
+ continue;
+ }
+
+ Object value = null;
+
+ PlexusConfiguration config =
configuration.getChild(parameter.getName(), false);
+ if (config != null) {
+ String expression = config.getValue(null);
+
+ try {
+ value = expressionEvaluator.evaluate(expression);
+
+ if (value == null) {
+ value = config.getAttribute("default-value", null);
+ }
+ } catch (ExpressionEvaluationException e) {
+ String msg = "Error evaluating the expression '" +
expression + "' for configuration value '"
+ + configuration.getName() + "'";
+ throw new ComponentConfigurationException(configuration,
msg, e);
+ }
+ }
+
+ if (value == null && (config == null || config.getChildCount() <=
0)) {
+ invalidParameters.add(parameter);
+ }
+ }
+
+ if (!invalidParameters.isEmpty()) {
+ throw new PluginParameterException(mojoDescriptor,
invalidParameters);
+ }
+ }
+
+ public void releaseMojo(Object mojo, MojoExecution mojoExecution) {
+ if (mojo != null) {
+ try {
+ container.release(mojo);
+ } catch (ComponentLifecycleException e) {
+ String goalExecId = mojoExecution.getGoal();
+
+ if (mojoExecution.getExecutionId() != null) {
+ goalExecId += " {execution: " +
mojoExecution.getExecutionId() + "}";
+ }
+
+ logger.debug("Error releasing mojo for " + goalExecId, e);
+ }
+ }
+ }
+
+ public ExtensionRealmCache.CacheRecord setupExtensionsRealm(
+ MavenProject project, Plugin plugin, RepositorySystemSession
session) throws PluginManagerException {
+ @SuppressWarnings("unchecked")
+ Map<String, ExtensionRealmCache.CacheRecord> pluginRealms =
+ (Map<String, ExtensionRealmCache.CacheRecord>)
project.getContextValue(KEY_EXTENSIONS_REALMS);
+ if (pluginRealms == null) {
+ pluginRealms = new HashMap<>();
+ project.setContextValue(KEY_EXTENSIONS_REALMS, pluginRealms);
+ }
+
+ final String pluginKey = plugin.getId();
+
+ ExtensionRealmCache.CacheRecord extensionRecord =
pluginRealms.get(pluginKey);
+ if (extensionRecord != null) {
+ return extensionRecord;
+ }
+
+ final List<RemoteRepository> repositories =
project.getRemotePluginRepositories();
+
+ // resolve plugin version as necessary
+ if (plugin.getVersion() == null) {
+ PluginVersionRequest versionRequest = new
DefaultPluginVersionRequest(plugin, session, repositories);
+ try {
+
plugin.setVersion(pluginVersionResolver.resolve(versionRequest).getVersion());
+ } catch (PluginVersionResolutionException e) {
+ throw new PluginManagerException(plugin, e.getMessage(), e);
+ }
+ }
+
+ // resolve plugin artifacts
+ List<Artifact> artifacts;
+ PluginArtifactsCache.Key cacheKey =
pluginArtifactsCache.createKey(plugin, null, repositories, session);
+ PluginArtifactsCache.CacheRecord recordArtifacts;
+ try {
+ recordArtifacts = pluginArtifactsCache.get(cacheKey);
+ } catch (PluginResolutionException e) {
+ throw new PluginManagerException(plugin, e.getMessage(), e);
+ }
+ if (recordArtifacts != null) {
+ artifacts = recordArtifacts.getArtifacts();
+ } else {
+ try {
+ artifacts = resolveExtensionArtifacts(plugin, repositories,
session);
+ recordArtifacts = pluginArtifactsCache.put(cacheKey,
artifacts);
+ } catch (PluginResolutionException e) {
+ pluginArtifactsCache.put(cacheKey, e);
+ pluginArtifactsCache.register(project, cacheKey,
recordArtifacts);
+ throw new PluginManagerException(plugin, e.getMessage(), e);
+ }
+ }
+ pluginArtifactsCache.register(project, cacheKey, recordArtifacts);
+
+ // create and cache extensions realms
+ final ExtensionRealmCache.Key extensionKey =
extensionRealmCache.createKey(artifacts);
+ extensionRecord = extensionRealmCache.get(extensionKey);
+ if (extensionRecord == null) {
+ ClassRealm extensionRealm =
classRealmManager.createExtensionRealm(plugin, toAetherArtifacts(artifacts));
+
+ // TODO figure out how to use the same PluginDescriptor when
running mojos
+
+ PluginDescriptor pluginDescriptor = null;
+ if (plugin.isExtensions() && !artifacts.isEmpty()) {
+ // ignore plugin descriptor parsing errors at this point
+ // these errors will reported during calculation of project
build execution plan
+ try {
+ pluginDescriptor =
extractPluginDescriptor(artifacts.get(0), plugin);
+ } catch (PluginDescriptorParsingException |
InvalidPluginDescriptorException e) {
+ // ignore, see above
+ }
+ }
+
+ discoverPluginComponents(extensionRealm, plugin, pluginDescriptor);
+
+ ExtensionDescriptor extensionDescriptor = null;
+ Artifact extensionArtifact = artifacts.get(0);
+ try {
+ extensionDescriptor =
extensionDescriptorBuilder.build(extensionArtifact.getFile());
+ } catch (IOException e) {
+ String message = "Invalid extension descriptor for " +
plugin.getId() + ": " + e.getMessage();
+ if (logger.isDebugEnabled()) {
+ logger.error(message, e);
+ } else {
+ logger.error(message);
+ }
+ }
+ extensionRecord = extensionRealmCache.put(extensionKey,
extensionRealm, extensionDescriptor, artifacts);
+ }
+ extensionRealmCache.register(project, extensionKey, extensionRecord);
+ pluginRealms.put(pluginKey, extensionRecord);
+
+ return extensionRecord;
+ }
+
+ private List<Artifact> resolveExtensionArtifacts(
+ Plugin extensionPlugin, List<RemoteRepository> repositories,
RepositorySystemSession session)
+ throws PluginResolutionException {
+ DependencyNode root =
pluginDependenciesResolver.resolve(extensionPlugin, null, null, repositories,
session);
+ PreorderNodeListGenerator nlg = new PreorderNodeListGenerator();
+ root.accept(nlg);
+ return toMavenArtifacts(root, nlg);
+ }
+}
diff --git
a/daemon-m39/src/main/java/org/mvndaemon/mvnd/plugin/ValidatingConfigurationListener.java
b/daemon-m39/src/main/java/org/mvndaemon/mvnd/plugin/ValidatingConfigurationListener.java
new file mode 100644
index 00000000..06d8b65f
--- /dev/null
+++
b/daemon-m39/src/main/java/org/mvndaemon/mvnd/plugin/ValidatingConfigurationListener.java
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+package org.mvndaemon.mvnd.plugin;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.maven.plugin.descriptor.MojoDescriptor;
+import org.apache.maven.plugin.descriptor.Parameter;
+import org.codehaus.plexus.component.configurator.ConfigurationListener;
+
+/**
+ * A configuration listener to help validate the plugin configuration. For
instance, check for required but missing
+ * parameters.
+ *
+ * @author Benjamin Bentmann
+ */
+class ValidatingConfigurationListener implements ConfigurationListener {
+
+ private final Object mojo;
+
+ private final ConfigurationListener delegate;
+
+ private final Map<String, Parameter> missingParameters;
+
+ ValidatingConfigurationListener(Object mojo, MojoDescriptor
mojoDescriptor, ConfigurationListener delegate) {
+ this.mojo = mojo;
+ this.delegate = delegate;
+ this.missingParameters = new HashMap<>();
+
+ if (mojoDescriptor.getParameters() != null) {
+ for (Parameter param : mojoDescriptor.getParameters()) {
+ if (param.isRequired()) {
+ missingParameters.put(param.getName(), param);
+ }
+ }
+ }
+ }
+
+ public Collection<Parameter> getMissingParameters() {
+ return missingParameters.values();
+ }
+
+ public void notifyFieldChangeUsingSetter(String fieldName, Object value,
Object target) {
+ delegate.notifyFieldChangeUsingSetter(fieldName, value, target);
+
+ if (mojo == target) {
+ notify(fieldName, value);
+ }
+ }
+
+ public void notifyFieldChangeUsingReflection(String fieldName, Object
value, Object target) {
+ delegate.notifyFieldChangeUsingReflection(fieldName, value, target);
+
+ if (mojo == target) {
+ notify(fieldName, value);
+ }
+ }
+
+ private void notify(String fieldName, Object value) {
+ if (value != null) {
+ missingParameters.remove(fieldName);
+ }
+ }
+}
diff --git a/daemon-m40/pom.xml b/daemon-m40/pom.xml
new file mode 100644
index 00000000..3500cb29
--- /dev/null
+++ b/daemon-m40/pom.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ 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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.apache.maven.daemon</groupId>
+ <artifactId>mvnd</artifactId>
+ <version>1.0.0-m5-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>mvnd-daemon-m40</artifactId>
+
+ <packaging>jar</packaging>
+ <name>Maven Daemon - Daemon 4.0.x specifics</name>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.maven</groupId>
+ <artifactId>maven-core</artifactId>
+ <version>${maven4.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.maven</groupId>
+ <artifactId>maven-embedder</artifactId>
+ <version>${maven4.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.maven.daemon</groupId>
+ <artifactId>mvnd-daemon</artifactId>
+ </dependency>
+ </dependencies>
+
+</project>
diff --git a/daemon/src/main/java/org/apache/maven/cli/DaemonMavenCli.java
b/daemon-m40/src/main/java/org/apache/maven/cli/DaemonMavenCli.java
similarity index 99%
rename from daemon/src/main/java/org/apache/maven/cli/DaemonMavenCli.java
rename to daemon-m40/src/main/java/org/apache/maven/cli/DaemonMavenCli.java
index 940d6180..92e13383 100644
--- a/daemon/src/main/java/org/apache/maven/cli/DaemonMavenCli.java
+++ b/daemon-m40/src/main/java/org/apache/maven/cli/DaemonMavenCli.java
@@ -70,8 +70,6 @@ import org.apache.maven.lifecycle.LifecycleExecutionException;
import org.apache.maven.model.building.ModelProcessor;
import org.apache.maven.plugin.ExtensionRealmCache;
import org.apache.maven.plugin.PluginArtifactsCache;
-import org.apache.maven.plugin.PluginRealmCache;
-import org.apache.maven.plugin.version.PluginVersionResolver;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.artifact.ProjectArtifactsCache;
import org.apache.maven.properties.internal.SystemProperties;
@@ -93,7 +91,6 @@ import org.codehaus.plexus.util.StringUtils;
import org.eclipse.aether.transfer.TransferListener;
import org.mvndaemon.mvnd.cache.invalidating.InvalidatingExtensionRealmCache;
import org.mvndaemon.mvnd.cache.invalidating.InvalidatingPluginArtifactsCache;
-import org.mvndaemon.mvnd.cache.invalidating.InvalidatingPluginRealmCache;
import org.mvndaemon.mvnd.cache.invalidating.InvalidatingProjectArtifactsCache;
import org.mvndaemon.mvnd.cli.EnvHelper;
import org.mvndaemon.mvnd.common.Environment;
@@ -102,7 +99,6 @@ import
org.mvndaemon.mvnd.logging.internal.Slf4jLoggerManager;
import org.mvndaemon.mvnd.logging.smart.BuildEventListener;
import org.mvndaemon.mvnd.logging.smart.LoggingExecutionListener;
import org.mvndaemon.mvnd.logging.smart.LoggingOutputStream;
-import org.mvndaemon.mvnd.plugin.CachingPluginVersionResolver;
import org.mvndaemon.mvnd.transfer.DaemonMavenTransferListener;
import org.slf4j.ILoggerFactory;
import org.slf4j.Logger;
@@ -119,7 +115,7 @@ import static
org.apache.maven.shared.utils.logging.MessageUtils.buffer;
*
* @author Jason van Zyl
*/
-public class DaemonMavenCli {
+public class DaemonMavenCli implements DaemonCli {
public static final String LOCAL_REPO_PROPERTY = "maven.repo.local";
public static final String MULTIMODULE_PROJECT_DIRECTORY =
"maven.multiModuleProjectDirectory";
@@ -522,9 +518,9 @@ public class DaemonMavenCli {
bind(CoreExportsProvider.class).toInstance(new
CoreExportsProvider(exports));
bind(ExtensionRealmCache.class).to(InvalidatingExtensionRealmCache.class);
bind(PluginArtifactsCache.class).to(InvalidatingPluginArtifactsCache.class);
-
bind(PluginRealmCache.class).to(InvalidatingPluginRealmCache.class);
+ //
bind(PluginRealmCache.class).to(InvalidatingPluginRealmCache.class);
bind(ProjectArtifactsCache.class).to(InvalidatingProjectArtifactsCache.class);
-
bind(PluginVersionResolver.class).to(CachingPluginVersionResolver.class);
+ //
bind(PluginVersionResolver.class).to(CachingPluginVersionResolver.class);
}
});
diff --git
a/daemon/src/main/java/org/apache/maven/project/SnapshotModelCache.java
b/daemon-m40/src/main/java/org/apache/maven/project/SnapshotModelCache.java
similarity index 92%
rename from
daemon/src/main/java/org/apache/maven/project/SnapshotModelCache.java
rename to
daemon-m40/src/main/java/org/apache/maven/project/SnapshotModelCache.java
index 39ff8b04..275fc97f 100644
--- a/daemon/src/main/java/org/apache/maven/project/SnapshotModelCache.java
+++ b/daemon-m40/src/main/java/org/apache/maven/project/SnapshotModelCache.java
@@ -18,6 +18,8 @@
*/
package org.apache.maven.project;
+import java.util.Objects;
+
import org.apache.maven.building.Source;
import org.apache.maven.model.building.ModelCache;
@@ -27,8 +29,8 @@ public class SnapshotModelCache implements ModelCache {
private final ModelCache reactorCache;
public SnapshotModelCache(ModelCache globalCache, ModelCache reactorCache)
{
- this.globalCache = globalCache;
- this.reactorCache = reactorCache;
+ this.globalCache = Objects.requireNonNull(globalCache);
+ this.reactorCache = Objects.requireNonNull(reactorCache);
}
public Object get(Source path, String tag) {
diff --git
a/daemon/src/main/java/org/apache/maven/project/SnapshotModelCacheFactory.java
b/daemon-m40/src/main/java/org/apache/maven/project/SnapshotModelCacheFactory.java
similarity index 100%
rename from
daemon/src/main/java/org/apache/maven/project/SnapshotModelCacheFactory.java
rename to
daemon-m40/src/main/java/org/apache/maven/project/SnapshotModelCacheFactory.java
diff --git
a/daemon/src/main/java/org/apache/maven/settings/SettingsUtilsV4.java
b/daemon-m40/src/main/java/org/apache/maven/settings/SettingsUtilsV4.java
similarity index 100%
rename from daemon/src/main/java/org/apache/maven/settings/SettingsUtilsV4.java
rename to
daemon-m40/src/main/java/org/apache/maven/settings/SettingsUtilsV4.java
diff --git
a/daemon/src/main/java/org/mvndaemon/mvnd/cache/invalidating/InvalidatingPluginDescriptorCache.java
b/daemon-m40/src/main/java/org/mvndaemon/mvnd/cache/invalidating/InvalidatingPluginDescriptorCache.java
similarity index 100%
rename from
daemon/src/main/java/org/mvndaemon/mvnd/cache/invalidating/InvalidatingPluginDescriptorCache.java
rename to
daemon-m40/src/main/java/org/mvndaemon/mvnd/cache/invalidating/InvalidatingPluginDescriptorCache.java
diff --git
a/daemon/src/main/java/org/mvndaemon/mvnd/cache/invalidating/InvalidatingPluginRealmCache.java
b/daemon-m40/src/main/java/org/mvndaemon/mvnd/cache/invalidating/InvalidatingPluginRealmCache.java
similarity index 100%
rename from
daemon/src/main/java/org/mvndaemon/mvnd/cache/invalidating/InvalidatingPluginRealmCache.java
rename to
daemon-m40/src/main/java/org/mvndaemon/mvnd/cache/invalidating/InvalidatingPluginRealmCache.java
diff --git
a/daemon/src/main/java/org/mvndaemon/mvnd/cache/invalidating/InvalidatingRealmCacheEventSpy.java
b/daemon-m40/src/main/java/org/mvndaemon/mvnd/cache/invalidating/InvalidatingRealmCacheEventSpy.java
similarity index 100%
rename from
daemon/src/main/java/org/mvndaemon/mvnd/cache/invalidating/InvalidatingRealmCacheEventSpy.java
rename to
daemon-m40/src/main/java/org/mvndaemon/mvnd/cache/invalidating/InvalidatingRealmCacheEventSpy.java
diff --git
a/daemon/src/main/java/org/mvndaemon/mvnd/plugin/CachingPluginVersionResolver.java
b/daemon-m40/src/main/java/org/mvndaemon/mvnd/plugin/CachingPluginVersionResolver.java
similarity index 100%
rename from
daemon/src/main/java/org/mvndaemon/mvnd/plugin/CachingPluginVersionResolver.java
rename to
daemon-m40/src/main/java/org/mvndaemon/mvnd/plugin/CachingPluginVersionResolver.java
diff --git a/daemon/pom.xml b/daemon/pom.xml
index afb97832..d4d23522 100644
--- a/daemon/pom.xml
+++ b/daemon/pom.xml
@@ -28,7 +28,7 @@
<artifactId>mvnd-daemon</artifactId>
<packaging>jar</packaging>
- <name>Maven Daemon</name>
+ <name>Maven Daemon - Daemon</name>
<dependencies>
<dependency>
diff --git a/daemon/src/main/java/org/apache/maven/cli/DaemonCli.java
b/daemon/src/main/java/org/apache/maven/cli/DaemonCli.java
new file mode 100644
index 00000000..e0815167
--- /dev/null
+++ b/daemon/src/main/java/org/apache/maven/cli/DaemonCli.java
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+package org.apache.maven.cli;
+
+import java.util.List;
+import java.util.Map;
+
+import org.mvndaemon.mvnd.logging.smart.BuildEventListener;
+
+/**
+ * Simple interface to bridge maven 3.9.x and 4.0.x CLI
+ */
+public interface DaemonCli {
+ int main(
+ List<String> args,
+ String workingDir,
+ String projectDir,
+ Map<String, String> env,
+ BuildEventListener buildEventListener)
+ throws Exception;
+}
diff --git a/daemon/src/main/java/org/mvndaemon/mvnd/daemon/Server.java
b/daemon/src/main/java/org/mvndaemon/mvnd/daemon/Server.java
index a10502f0..0bbf27ae 100644
--- a/daemon/src/main/java/org/mvndaemon/mvnd/daemon/Server.java
+++ b/daemon/src/main/java/org/mvndaemon/mvnd/daemon/Server.java
@@ -50,7 +50,7 @@ import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
-import org.apache.maven.cli.DaemonMavenCli;
+import org.apache.maven.cli.DaemonCli;
import org.mvndaemon.mvnd.builder.SmartBuilder;
import org.mvndaemon.mvnd.common.DaemonConnection;
import org.mvndaemon.mvnd.common.DaemonException;
@@ -87,7 +87,7 @@ public class Server implements AutoCloseable, Runnable {
private final String daemonId;
private final boolean noDaemon;
private final ServerSocketChannel socket;
- private final DaemonMavenCli cli;
+ private final DaemonCli cli;
private volatile DaemonInfo info;
private final DaemonRegistry registry;
@@ -129,7 +129,11 @@ public class Server implements AutoCloseable, Runnable {
.orElse(SocketFamily.inet);
try {
- cli = new DaemonMavenCli();
+ cli = (DaemonCli) getClass()
+ .getClassLoader()
+ .loadClass("org.apache.maven.cli.DaemonMavenCli")
+ .getDeclaredConstructor()
+ .newInstance();
registry = new DaemonRegistry(Environment.MVND_REGISTRY.asPath());
socket = socketFamily.openServerSocket();
executor = Executors.newScheduledThreadPool(1);
diff --git a/dist/pom.xml b/dist-m39/pom.xml
similarity index 88%
copy from dist/pom.xml
copy to dist-m39/pom.xml
index 46aeb88e..464fc7e9 100644
--- a/dist/pom.xml
+++ b/dist-m39/pom.xml
@@ -25,10 +25,10 @@
<version>1.0.0-m5-SNAPSHOT</version>
</parent>
- <artifactId>mvnd-dist</artifactId>
+ <artifactId>mvnd-dist-m39</artifactId>
<packaging>pom</packaging>
- <name>Maven Daemon - Distribution</name>
+ <name>Maven Daemon - Distribution for 3.9.x</name>
<properties>
<maven.compiler.target>11</maven.compiler.target>
@@ -52,6 +52,10 @@
<groupId>org.apache.maven.daemon</groupId>
<artifactId>mvnd-daemon</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.apache.maven.daemon</groupId>
+ <artifactId>mvnd-daemon-m39</artifactId>
+ </dependency>
</dependencies>
<build>
@@ -67,7 +71,7 @@
</goals>
<phase>package</phase>
<configuration>
-
<outputDirectory>${project.build.directory}/maven-mvnd-${project.version}-${os.detected.name}-${os.detected.arch}</outputDirectory>
+
<outputDirectory>${project.build.directory}/maven-${project.version}-mvnd-m39-${os.detected.name}-${os.detected.arch}</outputDirectory>
</configuration>
</execution>
</executions>
diff --git a/dist/src/main/provisio/maven-distro.xml
b/dist-m39/src/main/provisio/maven-distro.xml
similarity index 80%
copy from dist/src/main/provisio/maven-distro.xml
copy to dist-m39/src/main/provisio/maven-distro.xml
index 93647c4b..db3c73ff 100644
--- a/dist/src/main/provisio/maven-distro.xml
+++ b/dist-m39/src/main/provisio/maven-distro.xml
@@ -18,9 +18,9 @@
<assembly>
<artifactSet to="/">
- <artifact id="org.apache.maven:apache-maven:tar.gz:bin">
+ <artifact
id="org.apache.maven:apache-maven:tar.gz:bin:${maven3.version}">
<unpack useRoot="false"
-
excludes="conf/logging/*,lib/maven-slf4j-provider*,lib/plexus-utils-3.*" />
+ excludes="conf/logging/*,lib/maven-slf4j-provider*" />
</artifact>
</artifactSet>
@@ -37,6 +37,9 @@
<artifact id="org.apache.maven.daemon:mvnd-daemon:${project.version}">
<exclusion id="*:*"/>
</artifact>
+ <artifact
id="org.apache.maven.daemon:mvnd-daemon-m39:${project.version}">
+ <exclusion id="*:*"/>
+ </artifact>
<artifact id="org.apache.maven.daemon:mvnd-client:${project.version}">
<exclusion id="*:*"/>
</artifact>
@@ -64,7 +67,7 @@
</artifactSet>
<fileSet to="/">
- <directory path="${basedir}/src/main/distro"/>
+ <directory path="${basedir}/../dist/src/main/distro"/>
<directory path="${basedir}/..">
<include>NOTICE.txt</include>
<include>LICENSE.txt</include>
@@ -76,13 +79,15 @@
<include>mvnd</include>
<include>mvnd.exe</include>
</directory>
- <file touch="platform-${os.detected.name}-${os.detected.arch}"/>
+ <directory path="${basedir}/../dist/src/main/resources">
+ <include>platform-${os.detected.name}-${os.detected.arch}</include>
+ </directory>
</fileSet>
- <archive
name="maven-mvnd-${project.version}-${os.detected.name}-${os.detected.arch}.zip"
+ <archive
name="maven-${project.version}-mvnd-mvn39-${os.detected.name}-${os.detected.arch}.zip"
executable="**/bin/mvnd"/>
- <archive
name="maven-mvnd-${project.version}-${os.detected.name}-${os.detected.arch}.tar.gz"
+ <archive
name="maven-${project.version}-mvnd-mvn39-${os.detected.name}-${os.detected.arch}.tar.gz"
executable="**/bin/mvnd"/>
</assembly>
diff --git a/dist/pom.xml b/dist-m40/pom.xml
similarity index 88%
rename from dist/pom.xml
rename to dist-m40/pom.xml
index 46aeb88e..e9516be4 100644
--- a/dist/pom.xml
+++ b/dist-m40/pom.xml
@@ -25,10 +25,10 @@
<version>1.0.0-m5-SNAPSHOT</version>
</parent>
- <artifactId>mvnd-dist</artifactId>
+ <artifactId>mvnd-dist-m40</artifactId>
<packaging>pom</packaging>
- <name>Maven Daemon - Distribution</name>
+ <name>Maven Daemon - Distribution for 4.0.x</name>
<properties>
<maven.compiler.target>11</maven.compiler.target>
@@ -52,6 +52,10 @@
<groupId>org.apache.maven.daemon</groupId>
<artifactId>mvnd-daemon</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.apache.maven.daemon</groupId>
+ <artifactId>mvnd-daemon-m40</artifactId>
+ </dependency>
</dependencies>
<build>
@@ -67,7 +71,7 @@
</goals>
<phase>package</phase>
<configuration>
-
<outputDirectory>${project.build.directory}/maven-mvnd-${project.version}-${os.detected.name}-${os.detected.arch}</outputDirectory>
+
<outputDirectory>${project.build.directory}/maven-${project.version}-mvnd-m40-${os.detected.name}-${os.detected.arch}</outputDirectory>
</configuration>
</execution>
</executions>
diff --git a/dist/src/main/provisio/maven-distro.xml
b/dist-m40/src/main/provisio/maven-distro.xml
similarity index 81%
rename from dist/src/main/provisio/maven-distro.xml
rename to dist-m40/src/main/provisio/maven-distro.xml
index 93647c4b..05b775a3 100644
--- a/dist/src/main/provisio/maven-distro.xml
+++ b/dist-m40/src/main/provisio/maven-distro.xml
@@ -18,7 +18,7 @@
<assembly>
<artifactSet to="/">
- <artifact id="org.apache.maven:apache-maven:tar.gz:bin">
+ <artifact
id="org.apache.maven:apache-maven:tar.gz:bin:${maven4.version}">
<unpack useRoot="false"
excludes="conf/logging/*,lib/maven-slf4j-provider*,lib/plexus-utils-3.*" />
</artifact>
@@ -37,6 +37,9 @@
<artifact id="org.apache.maven.daemon:mvnd-daemon:${project.version}">
<exclusion id="*:*"/>
</artifact>
+ <artifact
id="org.apache.maven.daemon:mvnd-daemon-m40:${project.version}">
+ <exclusion id="*:*"/>
+ </artifact>
<artifact id="org.apache.maven.daemon:mvnd-client:${project.version}">
<exclusion id="*:*"/>
</artifact>
@@ -64,7 +67,7 @@
</artifactSet>
<fileSet to="/">
- <directory path="${basedir}/src/main/distro"/>
+ <directory path="${basedir}/../dist/src/main/distro"/>
<directory path="${basedir}/..">
<include>NOTICE.txt</include>
<include>LICENSE.txt</include>
@@ -76,13 +79,15 @@
<include>mvnd</include>
<include>mvnd.exe</include>
</directory>
- <file touch="platform-${os.detected.name}-${os.detected.arch}"/>
+ <directory path="${basedir}/../dist/src/main/resources">
+ <include>platform-${os.detected.name}-${os.detected.arch}</include>
+ </directory>
</fileSet>
- <archive
name="maven-mvnd-${project.version}-${os.detected.name}-${os.detected.arch}.zip"
+ <archive
name="maven-${project.version}-mvnd-40-${os.detected.name}-${os.detected.arch}.zip"
executable="**/bin/mvnd"/>
- <archive
name="maven-mvnd-${project.version}-${os.detected.name}-${os.detected.arch}.tar.gz"
+ <archive
name="maven-${project.version}-mvnd-40-${os.detected.name}-${os.detected.arch}.tar.gz"
executable="**/bin/mvnd"/>
</assembly>
diff --git a/dist/src/main/resources/platform-darwin-aarch64
b/dist/src/main/resources/platform-darwin-aarch64
new file mode 100644
index 00000000..90e7887e
--- /dev/null
+++ b/dist/src/main/resources/platform-darwin-aarch64
@@ -0,0 +1,13 @@
+ Copyright 2019-2021 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.
diff --git a/dist/src/main/resources/platform-darwin-amd64
b/dist/src/main/resources/platform-darwin-amd64
new file mode 100644
index 00000000..90e7887e
--- /dev/null
+++ b/dist/src/main/resources/platform-darwin-amd64
@@ -0,0 +1,13 @@
+ Copyright 2019-2021 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.
diff --git a/dist/src/main/resources/platform-linux-amd64
b/dist/src/main/resources/platform-linux-amd64
new file mode 100644
index 00000000..90e7887e
--- /dev/null
+++ b/dist/src/main/resources/platform-linux-amd64
@@ -0,0 +1,13 @@
+ Copyright 2019-2021 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.
diff --git a/dist/src/main/resources/platform-windows-amd64
b/dist/src/main/resources/platform-windows-amd64
new file mode 100644
index 00000000..90e7887e
--- /dev/null
+++ b/dist/src/main/resources/platform-windows-amd64
@@ -0,0 +1,13 @@
+ Copyright 2019-2021 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.
diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml
index 286d9a18..d88a5457 100644
--- a/integration-tests/pom.xml
+++ b/integration-tests/pom.xml
@@ -32,7 +32,8 @@
<properties>
<maven.compiler.target>11</maven.compiler.target>
<maven.compiler.source>11</maven.compiler.source>
-
<mvnd.home>${project.basedir}/../dist/target/maven-mvnd-${project.version}-${os.detected.name}-${os.detected.arch}</mvnd.home>
+
<mvnd.m39.home>${project.basedir}/../dist-m39/target/maven-${project.version}-mvnd-m39-${os.detected.name}-${os.detected.arch}</mvnd.m39.home>
+
<mvnd.m40.home>${project.basedir}/../dist-m40/target/maven-${project.version}-mvnd-m40-${os.detected.name}-${os.detected.arch}</mvnd.m40.home>
<preinstall.artifacts>org/apache/maven/surefire/surefire-providers/${surefire.version}
org/apache/maven/surefire/surefire-junit-platform/${surefire.version}
org/junit/platform/junit-platform-launcher/${junit-platform-launcher.version}
@@ -56,7 +57,19 @@
</dependency>
<dependency>
<groupId>org.apache.maven.daemon</groupId>
- <artifactId>mvnd-dist</artifactId>
+ <artifactId>mvnd-dist-m39</artifactId>
+ <type>pom</type>
+ <scope>test</scope>
+ <exclusions>
+ <exclusion>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-simple</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.maven.daemon</groupId>
+ <artifactId>mvnd-dist-m40</artifactId>
<type>pom</type>
<scope>test</scope>
<exclusions>
@@ -89,17 +102,50 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
- <systemPropertyVariables>
- <project.version>${project.version}</project.version>
- <mvnd.home>${mvnd.home}</mvnd.home>
- <mrm.repository.url>${mrm.repository.url}</mrm.repository.url>
- <os.detected.name>${os.detected.name}</os.detected.name>
- <os.detected.arch>${os.detected.arch}</os.detected.arch>
-
<mvnd.test.hostLocalMavenRepo>${settings.localRepository}</mvnd.test.hostLocalMavenRepo>
-
<preinstall.artifacts>${preinstall.artifacts}</preinstall.artifacts>
- </systemPropertyVariables>
- <rerunFailingTestsCount>2</rerunFailingTestsCount>
+ <rerunFailingTestsCount>4</rerunFailingTestsCount>
</configuration>
+ <executions>
+ <execution>
+ <id>default-test</id>
+ <phase>none</phase>
+ </execution>
+ <execution>
+ <id>mvn-39</id>
+ <goals>
+ <goal>test</goal>
+ </goals>
+ <phase>test</phase>
+ <configuration>
+ <systemPropertyVariables>
+ <mvnd.home>${mvnd.m39.home}</mvnd.home>
+ <project.version>${project.version}</project.version>
+ <mrm.repository.url>${mrm.repository.url}</mrm.repository.url>
+ <os.detected.name>${os.detected.name}</os.detected.name>
+ <os.detected.arch>${os.detected.arch}</os.detected.arch>
+
<mvnd.test.hostLocalMavenRepo>${settings.localRepository}</mvnd.test.hostLocalMavenRepo>
+
<preinstall.artifacts>${preinstall.artifacts}</preinstall.artifacts>
+ </systemPropertyVariables>
+ </configuration>
+ </execution>
+ <execution>
+ <id>mvn-40</id>
+ <goals>
+ <goal>test</goal>
+ </goals>
+ <phase>test</phase>
+ <configuration>
+ <systemPropertyVariables>
+ <mvnd.home>${mvnd.m40.home}</mvnd.home>
+ <project.version>${project.version}</project.version>
+ <mrm.repository.url>${mrm.repository.url}</mrm.repository.url>
+ <os.detected.name>${os.detected.name}</os.detected.name>
+ <os.detected.arch>${os.detected.arch}</os.detected.arch>
+
<mvnd.test.hostLocalMavenRepo>${settings.localRepository}</mvnd.test.hostLocalMavenRepo>
+
<preinstall.artifacts>${preinstall.artifacts}</preinstall.artifacts>
+ </systemPropertyVariables>
+ </configuration>
+ </execution>
+ </executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
@@ -168,6 +214,25 @@
<artifactId>maven-failsafe-plugin</artifactId>
<executions>
<execution>
+ <id>native-39</id>
+ <goals>
+ <goal>integration-test</goal>
+ <goal>verify</goal>
+ </goals>
+ <configuration>
+ <systemPropertyVariables>
+ <project.version>${project.version}</project.version>
+ <mvnd.home>${mvnd.m39.home}</mvnd.home>
+
<mrm.repository.url>${mrm.repository.url}</mrm.repository.url>
+ <os.detected.name>${os.detected.name}</os.detected.name>
+ <os.detected.arch>${os.detected.arch}</os.detected.arch>
+
<mvnd.test.hostLocalMavenRepo>${settings.localRepository}</mvnd.test.hostLocalMavenRepo>
+
<preinstall.artifacts>${preinstall.artifacts}</preinstall.artifacts>
+ </systemPropertyVariables>
+ </configuration>
+ </execution>
+ <execution>
+ <id>native-40</id>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
@@ -175,7 +240,7 @@
<configuration>
<systemPropertyVariables>
<project.version>${project.version}</project.version>
- <mvnd.home>${mvnd.home}</mvnd.home>
+ <mvnd.home>${mvnd.m40.home}</mvnd.home>
<mrm.repository.url>${mrm.repository.url}</mrm.repository.url>
<os.detected.name>${os.detected.name}</os.detected.name>
<os.detected.arch>${os.detected.arch}</os.detected.arch>
diff --git a/pom.xml b/pom.xml
index 25a2863f..a7bdad06 100644
--- a/pom.xml
+++ b/pom.xml
@@ -51,7 +51,10 @@
<module>common</module>
<module>client</module>
<module>daemon</module>
- <module>dist</module>
+ <module>daemon-m39</module>
+ <module>daemon-m40</module>
+ <module>dist-m39</module>
+ <module>dist-m40</module>
<module>integration-tests</module>
</modules>
@@ -85,6 +88,8 @@
<junit.jupiter.version>5.9.2</junit.jupiter.version>
<logback.version>1.2.11</logback.version>
<maven.version>4.0.0-alpha-4</maven.version>
+ <maven3.version>3.9.0</maven3.version>
+ <maven4.version>${maven.version}</maven4.version>
<!-- Keep in sync with Maven -->
<maven.resolver.version>1.9.4</maven.resolver.version>
<slf4j.version>1.7.36</slf4j.version>
@@ -251,7 +256,13 @@
</dependency>
<dependency>
<groupId>org.apache.maven.daemon</groupId>
- <artifactId>mvnd-dist</artifactId>
+ <artifactId>mvnd-dist-m39</artifactId>
+ <version>${project.version}</version>
+ <type>pom</type>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.maven.daemon</groupId>
+ <artifactId>mvnd-dist-m40</artifactId>
<version>${project.version}</version>
<type>pom</type>
</dependency>
@@ -260,6 +271,16 @@
<artifactId>mvnd-daemon</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.maven.daemon</groupId>
+ <artifactId>mvnd-daemon-m39</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.maven.daemon</groupId>
+ <artifactId>mvnd-daemon-m40</artifactId>
+ <version>${project.version}</version>
+ </dependency>
<dependency>
<groupId>org.apache.maven.daemon</groupId>
<artifactId>mvnd-helper-agent</artifactId>