diff --git a/.mvn/maven.config b/.mvn/maven.config index caa8f5f80c18..951ab8ff12f9 100644 --- a/.mvn/maven.config +++ b/.mvn/maven.config @@ -1 +1,4 @@ --DsessionRootDirectory=${session.rootDirectory} +-D +sessionRootDirectory=${session.rootDirectory} +-D +apache.snapshots diff --git a/impl/maven-executor/pom.xml b/impl/maven-executor/pom.xml deleted file mode 100644 index c4712962ce52..000000000000 --- a/impl/maven-executor/pom.xml +++ /dev/null @@ -1,149 +0,0 @@ - - - - 4.0.0 - - - org.apache.maven - maven-impl-modules - 4.1.0-SNAPSHOT - - - maven-executor - - Maven 4 Executor - Maven 4 Executor, for executing Maven 3/4. - - - 3.9.14 - ${project.version} - ${project.build.directory}/tmp - - - - - org.apache.maven - maven-api-annotations - provided - - - - org.junit.jupiter - junit-jupiter-api - test - - - org.junit.jupiter - junit-jupiter-params - test - - - eu.maveniverse.maven.mimir - testing - test - - - org.apache.maven - apache-maven - ${project.version} - bin - zip - test - - - * - * - - - - - - - - - org.apache.maven.plugins - maven-dependency-plugin - - - prepare-maven4-distro - - unpack-dependencies - - generate-test-resources - - apache-maven - - - - prepare-maven3-distro - - unpack - - generate-test-resources - - - - org.apache.maven - apache-maven - ${maven3version} - bin - zip - - - - - - - - org.apache.maven.plugins - maven-antrun-plugin - - - create-tmp-dir - - run - - process-test-resources - - - - - - - - - - org.apache.maven.plugins - maven-surefire-plugin - - false - - ${maven3version} - ${maven4version} - ${project.build.directory}/dependency/apache-maven-${maven3version} - ${project.build.directory}/dependency/apache-maven-${maven4version} - ${settings.localRepository} - - -Xmx256m @{jacocoArgLine} -Djava.io.tmpdir=${testTmpDir} - - - - - diff --git a/impl/maven-executor/src/main/java/org/apache/maven/cling/executor/Executor.java b/impl/maven-executor/src/main/java/org/apache/maven/cling/executor/Executor.java deleted file mode 100644 index 80107a9b8c87..000000000000 --- a/impl/maven-executor/src/main/java/org/apache/maven/cling/executor/Executor.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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.cling.executor; - -import org.apache.maven.api.annotations.Experimental; -import org.apache.maven.api.annotations.Nonnull; - -/** - * Defines the contract for a component responsible for executing a Maven tool - * using the information provided in an {@link ExecutorRequest}. This interface is central - * to the execution of Maven commands and builds, but it does not construct nor fully parses arguments. - * - * @since 4.0.0 - */ -@Experimental -public interface Executor extends AutoCloseable { - // Logic borrowed from Commons-Lang3 - boolean IS_WINDOWS = System.getProperty("os.name", "unknown").startsWith("Windows"); - - /** - * Maven version string returned when the actual version of Maven cannot be determined. - */ - String UNKNOWN_VERSION = "unknown"; - - /** - * Invokes the tool application using the provided {@link ExecutorRequest}. - * This method is responsible for executing the command or build - * process based on the information contained in the request. - * - * @param executorRequest the request containing all necessary information for the execution - * @return an integer representing the exit code of the execution (0 typically indicates success) - * @throws ExecutorException if an error occurs during the execution process - */ - int execute(@Nonnull ExecutorRequest executorRequest) throws ExecutorException; - - /** - * Returns the Maven version that provided {@link ExecutorRequest} point at (would use). This - * operation, depending on the underlying implementation, can be costly. If a caller uses this method often, it is - * the caller's responsibility to properly cache returned values. (key can be {@link ExecutorRequest#installationDirectory()}. - * - * @param executorRequest the request containing all necessary information for the execution - * @return a string representing the Maven version or {@link #UNKNOWN_VERSION} - * @throws ExecutorException if an error occurs during the execution process - */ - @Nonnull - String mavenVersion(@Nonnull ExecutorRequest executorRequest) throws ExecutorException; - - /** - * Closes and disposes of this {@link Executor} instance, releasing any resources it may hold. - * This method is called automatically when using try-with-resources statements. - * - *

The default implementation does nothing. Subclasses should override this method - * if they need to perform cleanup operations.

- * - * @throws ExecutorException if an error occurs while closing the {@link Executor} - */ - @Override - void close() throws ExecutorException; -} diff --git a/impl/maven-executor/src/main/java/org/apache/maven/cling/executor/ExecutorException.java b/impl/maven-executor/src/main/java/org/apache/maven/cling/executor/ExecutorException.java deleted file mode 100644 index 8d8fbfde43fb..000000000000 --- a/impl/maven-executor/src/main/java/org/apache/maven/cling/executor/ExecutorException.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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.cling.executor; - -import org.apache.maven.api.annotations.Experimental; -import org.apache.maven.api.annotations.Nullable; - -/** - * Represents an exception that occurs during the execution of a Maven build or command. - * This exception is typically thrown when there are errors during the execution of a Maven - * process, such as wrong cwd, non-existent installation directory, or other runtime issues. - * - * @since 4.0.0 - */ -@Experimental -public class ExecutorException extends RuntimeException { - /** - * Constructs a new {@code InvokerException} with the specified detail message. - * - * @param message the detail message explaining the cause of the exception - */ - public ExecutorException(@Nullable String message) { - super(message); - } - - /** - * Constructs a new {@code InvokerException} with the specified detail message and cause. - * - * @param message the detail message explaining the cause of the exception - * @param cause the underlying cause of the exception - */ - public ExecutorException(@Nullable String message, @Nullable Throwable cause) { - super(message, cause); - } -} diff --git a/impl/maven-executor/src/main/java/org/apache/maven/cling/executor/ExecutorHelper.java b/impl/maven-executor/src/main/java/org/apache/maven/cling/executor/ExecutorHelper.java deleted file mode 100644 index 8aea8b2a528c..000000000000 --- a/impl/maven-executor/src/main/java/org/apache/maven/cling/executor/ExecutorHelper.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * 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.cling.executor; - -import org.apache.maven.api.annotations.Nonnull; - -/** - * Helper class for routing Maven execution based on preferences and/or issued execution requests. - */ -public interface ExecutorHelper { - /** - * The modes of execution. - */ - enum Mode { - /** - * Automatically decide. For example, presence of {@link ExecutorRequest#environmentVariables()} or - * {@link ExecutorRequest#jvmArguments()} will result in choosing {@link #FORKED} executor. Otherwise, - * {@link #EMBEDDED} executor is preferred. - */ - AUTO, - /** - * Forces embedded execution. May fail if {@link ExecutorRequest} contains input unsupported by executor. - */ - EMBEDDED, - /** - * Forces forked execution. Always carried out, most isolated and "most correct", but is slow as it uses child process. - */ - FORKED - } - - /** - * Returns the preferred mode of this helper. - */ - @Nonnull - Mode getDefaultMode(); - - /** - * Creates pre-populated builder for {@link ExecutorRequest}. Users of helper must use this method to create - * properly initialized request builder. - */ - @Nonnull - ExecutorRequest.Builder executorRequest(); - - /** - * Executes the request with preferred mode executor. - */ - default int execute(ExecutorRequest executorRequest) throws ExecutorException { - return execute(getDefaultMode(), executorRequest); - } - - /** - * Executes the request with passed in mode executor. - */ - int execute(Mode mode, ExecutorRequest executorRequest) throws ExecutorException; - - /** - * High level operation, returns the version of the Maven covered by this helper. This method call caches - * underlying operation, and is safe to invoke as many times needed. - * - * @see Executor#mavenVersion(ExecutorRequest) - */ - @Nonnull - String mavenVersion(); -} diff --git a/impl/maven-executor/src/main/java/org/apache/maven/cling/executor/ExecutorRequest.java b/impl/maven-executor/src/main/java/org/apache/maven/cling/executor/ExecutorRequest.java deleted file mode 100644 index 1e7118f0b627..000000000000 --- a/impl/maven-executor/src/main/java/org/apache/maven/cling/executor/ExecutorRequest.java +++ /dev/null @@ -1,518 +0,0 @@ -/* - * 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.cling.executor; - -import java.io.InputStream; -import java.io.OutputStream; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -import org.apache.maven.api.annotations.Experimental; -import org.apache.maven.api.annotations.Immutable; -import org.apache.maven.api.annotations.Nonnull; -import org.apache.maven.api.annotations.Nullable; - -import static java.util.Objects.requireNonNull; - -/** - * Represents a request to execute Maven with command-line arguments. - * This interface encapsulates all the necessary information needed to execute - * Maven command with arguments. The arguments are not parsed, they are just passed over - * to executed tool. - * - * @since 4.0.0 - */ -@Immutable -@Experimental -public interface ExecutorRequest { - /** - * The Maven command. - */ - String MVN = "mvn"; - - /** - * The command to execute, ie "mvn". - */ - @Nonnull - String command(); - - /** - * The immutable list of arguments to pass to the command. - */ - @Nonnull - List arguments(); - - /** - * Returns the current working directory for the Maven execution. - * This is typically the directory from which Maven was invoked. - * - * @return the current working directory path - */ - @Nonnull - Path cwd(); - - /** - * Returns the Maven installation directory. - * This is usually set by the Maven launcher script using the "maven.home" system property. - * - * @return the Maven installation directory path - */ - @Nonnull - Path installationDirectory(); - - /** - * Returns the user's home directory. - * This is typically obtained from the "user.home" system property. - * - * @return the user's home directory path - */ - @Nonnull - Path userHomeDirectory(); - - /** - * Returns the map of Java System Properties to set before executing process. - * - * @return an Optional containing the map of Java System Properties, or empty if not specified - */ - @Nonnull - Optional> jvmSystemProperties(); - - /** - * Returns the map of environment variables to set before executing process. - * This property is used ONLY by executors that spawn a new JVM. - * - * @return an Optional containing the map of environment variables, or empty if not specified - */ - @Nonnull - Optional> environmentVariables(); - - /** - * Returns the list of extra JVM arguments to be passed to the forked process. - * These arguments allow for customization of the JVM environment in which tool will run. - * This property is used ONLY by executors that spawn a new JVM. - * - * @return an Optional containing the list of extra JVM arguments, or empty if not specified - */ - @Nonnull - Optional> jvmArguments(); - - /** - * Optional provider for STD in of the Maven. If given, this provider will be piped into std input of - * Maven. - * - * @return an Optional containing the stdin provider, or empty if not specified. - */ - Optional stdIn(); - - /** - * Optional consumer for STD out of the Maven. If given, this consumer will get all output from the std out of - * Maven. Note: whether consumer gets to consume anything depends on invocation arguments passed in - * {@link #arguments()}, as if log file is set, not much will go to stdout. - * - * @return an Optional containing the stdout consumer, or empty if not specified. - */ - Optional stdOut(); - - /** - * Optional consumer for STD err of the Maven. If given, this consumer will get all output from the std err of - * Maven. Note: whether consumer gets to consume anything depends on invocation arguments passed in - * {@link #arguments()}, as if log file is set, not much will go to stderr. - * - * @return an Optional containing the stderr consumer, or empty if not specified. - */ - Optional stdErr(); - - /** - * Indicate if {@code ~/.mavenrc} should be skipped during execution. - *

- * Affected only for forked executor by adding MAVEN_SKIP_RC environment variable - */ - boolean skipMavenRc(); - - /** - * Returns {@link Builder} created from this instance. - */ - @Nonnull - default Builder toBuilder() { - return new Builder( - command(), - arguments(), - cwd(), - installationDirectory(), - userHomeDirectory(), - jvmSystemProperties().orElse(null), - environmentVariables().orElse(null), - jvmArguments().orElse(null), - stdIn().orElse(null), - stdOut().orElse(null), - stdErr().orElse(null), - skipMavenRc()); - } - - /** - * Returns new builder pre-set to run Maven. The discovery of maven home is attempted, user cwd and home are - * also discovered by standard means. - */ - @Nonnull - static Builder mavenBuilder(@Nullable Path installationDirectory) { - return new Builder( - MVN, - null, - getCanonicalPath(Paths.get(System.getProperty("user.dir"))), - installationDirectory != null - ? getCanonicalPath(installationDirectory) - : discoverInstallationDirectory(), - getCanonicalPath(Paths.get(System.getProperty("user.home"))), - null, - null, - null, - null, - null, - null, - false); - } - - class Builder { - private String command; - private List arguments; - private Path cwd; - private Path installationDirectory; - private Path userHomeDirectory; - private Map jvmSystemProperties; - private Map environmentVariables; - private List jvmArguments; - private InputStream stdIn; - private OutputStream stdOut; - private OutputStream stdErr; - private boolean skipMavenRc; - - private Builder() {} - - @SuppressWarnings("ParameterNumber") - private Builder( - String command, - List arguments, - Path cwd, - Path installationDirectory, - Path userHomeDirectory, - Map jvmSystemProperties, - Map environmentVariables, - List jvmArguments, - InputStream stdIn, - OutputStream stdOut, - OutputStream stdErr, - boolean skipMavenRc) { - this.command = command; - this.arguments = arguments; - this.cwd = cwd; - this.installationDirectory = installationDirectory; - this.userHomeDirectory = userHomeDirectory; - this.jvmSystemProperties = jvmSystemProperties; - this.environmentVariables = environmentVariables; - this.jvmArguments = jvmArguments; - this.stdIn = stdIn; - this.stdOut = stdOut; - this.stdErr = stdErr; - this.skipMavenRc = skipMavenRc; - } - - @Nonnull - public Builder command(String command) { - this.command = requireNonNull(command, "command"); - return this; - } - - @Nonnull - public Builder arguments(List arguments) { - this.arguments = requireNonNull(arguments, "arguments"); - return this; - } - - @Nonnull - public Builder argument(String argument) { - if (arguments == null) { - arguments = new ArrayList<>(); - } - this.arguments.add(requireNonNull(argument, "argument")); - return this; - } - - @Nonnull - public Builder cwd(Path cwd) { - this.cwd = getCanonicalPath(requireNonNull(cwd, "cwd")); - return this; - } - - @Nonnull - public Builder installationDirectory(Path installationDirectory) { - this.installationDirectory = - getCanonicalPath(requireNonNull(installationDirectory, "installationDirectory")); - return this; - } - - @Nonnull - public Builder userHomeDirectory(Path userHomeDirectory) { - this.userHomeDirectory = getCanonicalPath(requireNonNull(userHomeDirectory, "userHomeDirectory")); - return this; - } - - @Nonnull - public Builder jvmSystemProperties(Map jvmSystemProperties) { - this.jvmSystemProperties = jvmSystemProperties; - return this; - } - - @Nonnull - public Builder jvmSystemProperty(String key, String value) { - requireNonNull(key, "env key"); - requireNonNull(value, "env value"); - if (jvmSystemProperties == null) { - this.jvmSystemProperties = new HashMap<>(); - } - this.jvmSystemProperties.put(key, value); - return this; - } - - @Nonnull - public Builder environmentVariables(Map environmentVariables) { - this.environmentVariables = environmentVariables; - return this; - } - - @Nonnull - public Builder environmentVariable(String key, String value) { - requireNonNull(key, "env key"); - requireNonNull(value, "env value"); - if (environmentVariables == null) { - this.environmentVariables = new HashMap<>(); - } - this.environmentVariables.put(key, value); - return this; - } - - @Nonnull - public Builder jvmArguments(List jvmArguments) { - this.jvmArguments = jvmArguments; - return this; - } - - @Nonnull - public Builder jvmArgument(String jvmArgument) { - if (jvmArguments == null) { - jvmArguments = new ArrayList<>(); - } - this.jvmArguments.add(requireNonNull(jvmArgument, "jvmArgument")); - return this; - } - - @Nonnull - public Builder stdIn(InputStream stdIn) { - this.stdIn = stdIn; - return this; - } - - @Nonnull - public Builder stdOut(OutputStream stdOut) { - this.stdOut = stdOut; - return this; - } - - @Nonnull - public Builder stdErr(OutputStream stdErr) { - this.stdErr = stdErr; - return this; - } - - @Nonnull - public Builder skipMavenRc(boolean skipMavenRc) { - this.skipMavenRc = skipMavenRc; - return this; - } - - @Nonnull - public ExecutorRequest build() { - return new Impl( - command, - arguments, - cwd, - installationDirectory, - userHomeDirectory, - jvmSystemProperties, - environmentVariables, - jvmArguments, - stdIn, - stdOut, - stdErr, - skipMavenRc); - } - - private static class Impl implements ExecutorRequest { - private final String command; - private final List arguments; - private final Path cwd; - private final Path installationDirectory; - private final Path userHomeDirectory; - private final Map jvmSystemProperties; - private final Map environmentVariables; - private final List jvmArguments; - private final InputStream stdIn; - private final OutputStream stdOut; - private final OutputStream stdErr; - private final boolean skipMavenRc; - - @SuppressWarnings("ParameterNumber") - private Impl( - String command, - List arguments, - Path cwd, - Path installationDirectory, - Path userHomeDirectory, - Map jvmSystemProperties, - Map environmentVariables, - List jvmArguments, - InputStream stdIn, - OutputStream stdOut, - OutputStream stdErr, - boolean skipMavenRc) { - this.command = requireNonNull(command); - this.arguments = arguments == null ? List.of() : List.copyOf(arguments); - this.cwd = getCanonicalPath(requireNonNull(cwd)); - this.installationDirectory = getCanonicalPath(requireNonNull(installationDirectory)); - this.userHomeDirectory = getCanonicalPath(requireNonNull(userHomeDirectory)); - this.jvmSystemProperties = jvmSystemProperties != null && !jvmSystemProperties.isEmpty() - ? Map.copyOf(jvmSystemProperties) - : null; - this.environmentVariables = environmentVariables != null && !environmentVariables.isEmpty() - ? Map.copyOf(environmentVariables) - : null; - this.jvmArguments = jvmArguments != null && !jvmArguments.isEmpty() ? List.copyOf(jvmArguments) : null; - this.stdIn = stdIn; - this.stdOut = stdOut; - this.stdErr = stdErr; - this.skipMavenRc = skipMavenRc; - } - - @Override - public String command() { - return command; - } - - @Override - public List arguments() { - return arguments; - } - - @Override - public Path cwd() { - return cwd; - } - - @Override - public Path installationDirectory() { - return installationDirectory; - } - - @Override - public Path userHomeDirectory() { - return userHomeDirectory; - } - - @Override - public Optional> jvmSystemProperties() { - return Optional.ofNullable(jvmSystemProperties); - } - - @Override - public Optional> environmentVariables() { - return Optional.ofNullable(environmentVariables); - } - - @Override - public Optional> jvmArguments() { - return Optional.ofNullable(jvmArguments); - } - - @Override - public Optional stdIn() { - return Optional.ofNullable(stdIn); - } - - @Override - public Optional stdOut() { - return Optional.ofNullable(stdOut); - } - - @Override - public Optional stdErr() { - return Optional.ofNullable(stdErr); - } - - @Override - public boolean skipMavenRc() { - return skipMavenRc; - } - - @Override - public String toString() { - return getClass().getSimpleName() + "{" + "command='" - + command + '\'' + ", arguments=" - + arguments + ", cwd=" - + cwd + ", installationDirectory=" - + installationDirectory + ", userHomeDirectory=" - + userHomeDirectory + ", jvmSystemProperties=" - + jvmSystemProperties + ", environmentVariables=" - + environmentVariables + ", jvmArguments=" - + jvmArguments + ", stdinProvider=" - + stdIn + ", stdoutConsumer=" - + stdOut + ", stderrConsumer=" - + stdErr + ", skipMavenRc=" - + skipMavenRc + "}"; - } - } - } - - @Nonnull - static Path discoverInstallationDirectory() { - String mavenHome = System.getProperty("maven.home"); - if (mavenHome == null) { - throw new ExecutorException("requires maven.home Java System Property set"); - } - return getCanonicalPath(Paths.get(mavenHome)); - } - - @Nonnull - static Path discoverUserHomeDirectory() { - String userHome = System.getProperty("user.home"); - if (userHome == null) { - throw new ExecutorException("requires user.home Java System Property set"); - } - return getCanonicalPath(Paths.get(userHome)); - } - - @Nonnull - static Path getCanonicalPath(Path path) { - requireNonNull(path, "path"); - return path.toAbsolutePath().normalize(); - } -} diff --git a/impl/maven-executor/src/main/java/org/apache/maven/cling/executor/ExecutorTool.java b/impl/maven-executor/src/main/java/org/apache/maven/cling/executor/ExecutorTool.java deleted file mode 100644 index 4a9776742ff8..000000000000 --- a/impl/maven-executor/src/main/java/org/apache/maven/cling/executor/ExecutorTool.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * 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.cling.executor; - -import java.util.Map; - -import org.apache.maven.api.annotations.Nullable; - -/** - * A tool implementing some common Maven operations. - */ -public interface ExecutorTool { - /** - * Performs a diagnostic dump of the environment. - * - * @param request never {@code null} - */ - Map dump(ExecutorRequest.Builder request) throws ExecutorException; - - /** - * Returns the location of local repository, as detected by Maven. The {@code userSettings} param may contain - * some override (equivalent of {@code -s settings.xml} on CLI). - * - * @param request never {@code null} - */ - String localRepository(ExecutorRequest.Builder request) throws ExecutorException; - - /** - * Returns relative (to {@link #localRepository(ExecutorRequest.Builder)}) path of given artifact in local repository. - * - * @param request never {@code null} - * @param gav the usual resolver artifact GAV string, never {@code null} - * @param repositoryId the remote repository ID in case "remote artifact" is asked for - */ - String artifactPath(ExecutorRequest.Builder request, String gav, @Nullable String repositoryId) - throws ExecutorException; - - /** - * Returns relative (to {@link #localRepository(ExecutorRequest.Builder)}) path of given metadata in local repository. - * The metadata coordinates in form of {@code [G]:[A]:[V]:[type]}. Absence of {@code A} implies absence of {@code V} - * as well (in other words, it can be {@code G}, {@code G:A} or {@code G:A:V}). The absence of {@code type} implies - * it is "maven-metadata.xml". The simplest spec string is {@code :::}. - *

- * Examples: - *

    - *
  • {@code :::} is root metadata named "maven-metadata.xml"
  • - *
  • {@code :::my-metadata.xml} is root metadata named "my-metadata.xml"
  • - *
  • {@code G:::} equals to {@code G:::maven-metadata.xml}
  • - *
  • {@code G:A::} equals to {@code G:A::maven-metadata.xml}
  • - *
- * - * @param request never {@code null} - * @param gav the resolver metadata GAV string - * @param repositoryId the remote repository ID in case "remote metadata" is asked for - */ - String metadataPath(ExecutorRequest.Builder request, String gav, @Nullable String repositoryId) - throws ExecutorException; -} diff --git a/impl/maven-executor/src/main/java/org/apache/maven/cling/executor/embedded/EmbeddedMavenExecutor.java b/impl/maven-executor/src/main/java/org/apache/maven/cling/executor/embedded/EmbeddedMavenExecutor.java deleted file mode 100644 index 0e2ddaa4f6e1..000000000000 --- a/impl/maven-executor/src/main/java/org/apache/maven/cling/executor/embedded/EmbeddedMavenExecutor.java +++ /dev/null @@ -1,457 +0,0 @@ -/* - * 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.cling.executor.embedded; - -import java.io.Closeable; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.PrintStream; -import java.lang.reflect.Constructor; -import java.lang.reflect.Method; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLClassLoader; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Function; -import java.util.stream.Stream; - -import org.apache.maven.cling.executor.Executor; -import org.apache.maven.cling.executor.ExecutorException; -import org.apache.maven.cling.executor.ExecutorRequest; - -import static java.util.Objects.requireNonNull; - -/** - * Embedded executor implementation, that invokes Maven from installation directory within this same JVM but in isolated - * classloader. This class supports Maven 4.x and Maven 3.x as well. The ClassWorld of Maven is kept in memory as - * long as instance of this class is not closed. Subsequent execution requests over same installation home are cached. - */ -public class EmbeddedMavenExecutor implements Executor { - /** - * Maven4 supports multiple commands from same installation directory. - */ - protected static final Map MVN4_MAIN_CLASSES = Map.of( - "mvn", - "org.apache.maven.cling.MavenCling", - "mvnenc", - "org.apache.maven.cling.MavenEncCling", - "mvnsh", - "org.apache.maven.cling.MavenShellCling"); - - /** - * Context holds things loaded up from given Maven Installation Directory. - */ - protected static final class Context { - private final URLClassLoader bootClassLoader; - private final String version; - private final Object classWorld; - private final Set originalClassRealmIds; - private final ClassLoader tccl; - private final Map> commands; // the commands - private final Collection keepAlive; // refs things to make sure no GC takes it away - - private Context( - URLClassLoader bootClassLoader, - String version, - Object classWorld, - Set originalClassRealmIds, - ClassLoader tccl, - Map> commands, - Collection keepAlive) { - this.bootClassLoader = bootClassLoader; - this.version = version; - this.classWorld = classWorld; - this.originalClassRealmIds = originalClassRealmIds; - this.tccl = tccl; - this.commands = commands; - this.keepAlive = keepAlive; - } - } - - protected final boolean cacheContexts; - protected final boolean useMavenArgsEnv; - protected final AtomicBoolean closed; - protected final InputStream originalStdin; - protected final PrintStream originalStdout; - protected final PrintStream originalStderr; - protected final Properties originalProperties; - protected final ClassLoader originalClassLoader; - protected final ConcurrentHashMap contexts; - - public EmbeddedMavenExecutor() { - this(true, true); - } - - public EmbeddedMavenExecutor(boolean cacheContexts, boolean useMavenArgsEnv) { - this.cacheContexts = cacheContexts; - this.useMavenArgsEnv = useMavenArgsEnv; - this.closed = new AtomicBoolean(false); - this.originalStdin = System.in; - this.originalStdout = System.out; - this.originalStderr = System.err; - this.originalClassLoader = Thread.currentThread().getContextClassLoader(); - this.contexts = new ConcurrentHashMap<>(); - this.originalProperties = new Properties(); - this.originalProperties.putAll(System.getProperties()); - } - - @Override - public int execute(ExecutorRequest executorRequest) throws ExecutorException { - requireNonNull(executorRequest); - if (closed.get()) { - throw new ExecutorException("Executor is closed"); - } - validate(executorRequest); - Context context = mayCreate(executorRequest); - String command = executorRequest.command(); - Function exec = context.commands.get(command); - if (exec == null) { - throw new IllegalArgumentException( - "Unknown command: '" + command + "' for '" + executorRequest.installationDirectory() + "'"); - } - - Thread.currentThread().setContextClassLoader(context.tccl); - try { - return exec.apply(executorRequest); - } catch (Exception e) { - throw new ExecutorException("Failed to execute", e); - } finally { - try { - disposeRuntimeCreatedRealms(context); - } finally { - System.setIn(originalStdin); - System.setOut(originalStdout); - System.setErr(originalStderr); - Thread.currentThread().setContextClassLoader(originalClassLoader); - System.setProperties(originalProperties); - if (!cacheContexts) { - doClose(context); - } - } - } - } - - /** - * Unloads dynamically loaded things, like extensions created realms. Makes sure we go back to "initial state". - */ - protected void disposeRuntimeCreatedRealms(Context context) { - try { - Method getRealms = context.classWorld.getClass().getMethod("getRealms"); - Method disposeRealm = context.classWorld.getClass().getMethod("disposeRealm", String.class); - List realms = (List) getRealms.invoke(context.classWorld); - for (Object realm : realms) { - String realmId = (String) realm.getClass().getMethod("getId").invoke(realm); - if (!context.originalClassRealmIds.contains(realmId)) { - disposeRealm.invoke(context.classWorld, realmId); - } - } - } catch (Exception e) { - throw new ExecutorException("Failed to dispose runtime created realms", e); - } - } - - @Override - public String mavenVersion(ExecutorRequest executorRequest) throws ExecutorException { - requireNonNull(executorRequest); - if (closed.get()) { - throw new ExecutorException("Executor is closed"); - } - validate(executorRequest); - return mayCreate(executorRequest).version; - } - - protected Context mayCreate(ExecutorRequest executorRequest) { - Path mavenHome = ExecutorRequest.getCanonicalPath(executorRequest.installationDirectory()); - if (cacheContexts) { - return contexts.computeIfAbsent(mavenHome, k -> doCreate(mavenHome, executorRequest)); - } else { - return doCreate(mavenHome, executorRequest); - } - } - - protected Context doCreate(Path mavenHome, ExecutorRequest executorRequest) { - if (!Files.isDirectory(mavenHome)) { - throw new IllegalArgumentException("Installation directory must point to existing directory: " + mavenHome); - } - if (!MVN4_MAIN_CLASSES.containsKey(executorRequest.command())) { - throw new IllegalArgumentException( - getClass().getSimpleName() + " does not support command " + executorRequest.command()); - } - if (executorRequest.environmentVariables().isPresent()) { - throw new IllegalArgumentException(getClass().getSimpleName() + " does not support environment variables: " - + executorRequest.environmentVariables().get()); - } - if (executorRequest.jvmArguments().isPresent()) { - throw new IllegalArgumentException(getClass().getSimpleName() + " does not support jvmArguments: " - + executorRequest.jvmArguments().get()); - } - Path boot = mavenHome.resolve("boot"); - Path m2conf = mavenHome.resolve("bin/m2.conf"); - if (!Files.isDirectory(boot) || !Files.isRegularFile(m2conf)) { - throw new IllegalArgumentException( - "Installation directory does not point to Maven installation: " + mavenHome); - } - - ArrayList mavenArgs = new ArrayList<>(); - String mavenArgsEnv = System.getenv("MAVEN_ARGS"); - if (useMavenArgsEnv && mavenArgsEnv != null && !mavenArgsEnv.isEmpty()) { - Arrays.stream(mavenArgsEnv.split(" ")) - .filter(s -> !s.trim().isEmpty()) - .forEach(s -> mavenArgs.add(0, s)); - } - - Properties properties = prepareProperties(executorRequest); - // set ahead of time, if the mavenHome points to Maven4, as ClassWorld Launcher needs this property - properties.setProperty( - "maven.mainClass", requireNonNull(MVN4_MAIN_CLASSES.get(ExecutorRequest.MVN), "mainClass")); - System.setProperties(properties); - URLClassLoader bootClassLoader = createMavenBootClassLoader(boot, Collections.emptyList()); - Thread.currentThread().setContextClassLoader(bootClassLoader); - try { - Class launcherClass = bootClassLoader.loadClass("org.codehaus.plexus.classworlds.launcher.Launcher"); - Object launcher = launcherClass.getDeclaredConstructor().newInstance(); - Method configure = launcherClass.getMethod("configure", InputStream.class); - try (InputStream inputStream = Files.newInputStream(m2conf)) { - configure.invoke(launcher, inputStream); - } - Object classWorld = launcherClass.getMethod("getWorld").invoke(launcher); - Set originalClassRealmIds = new HashSet<>(); - - // collect pre-created (in m2.conf) class realms as "original ones"; the rest are created at runtime - Method getRealms = classWorld.getClass().getMethod("getRealms"); - List realms = (List) getRealms.invoke(classWorld); - for (Object realm : realms) { - Method realmGetId = realm.getClass().getMethod("getId"); - originalClassRealmIds.add((String) realmGetId.invoke(realm)); - } - - Class cliClass = - (Class) launcherClass.getMethod("getMainClass").invoke(launcher); - String version = getMavenVersion(cliClass); - Map> commands = new HashMap<>(); - ArrayList keepAlive = new ArrayList<>(); - - if (version.startsWith("3.")) { - // 3.x - if (!ExecutorRequest.MVN.equals(executorRequest.command())) { - throw new IllegalArgumentException(getClass().getSimpleName() + " w/ mvn3 does not support command " - + executorRequest.command()); - } - // 3.9.x - mayAddToKeepAlive(keepAlive, cliClass, "org.fusesource.jansi.internal.JansiLoader"); - // 3.10.x - mayAddToKeepAlive(keepAlive, cliClass, "org.jline.nativ.JLineNativeLoader"); - - Constructor newMavenCli = cliClass.getConstructor(classWorld.getClass()); - Object mavenCli = newMavenCli.newInstance(classWorld); - Class[] parameterTypes = {String[].class, String.class, PrintStream.class, PrintStream.class}; - Method doMain = cliClass.getMethod("doMain", parameterTypes); - commands.put(ExecutorRequest.MVN, r -> { - System.setProperties(prepareProperties(r)); - try { - ArrayList args = new ArrayList<>(mavenArgs); - args.addAll(r.arguments()); - PrintStream stdout = r.stdOut().isEmpty() - ? null - : new PrintStream(r.stdOut().orElseThrow(), true); - PrintStream stderr = r.stdErr().isEmpty() - ? null - : new PrintStream(r.stdErr().orElseThrow(), true); - return (int) doMain.invoke(mavenCli, new Object[] { - args.toArray(new String[0]), r.cwd().toString(), stdout, stderr - }); - } catch (Exception e) { - throw new ExecutorException("Failed to execute", e); - } - }); - } else { - // assume 4.x - mayAddToKeepAlive(keepAlive, cliClass, "org.jline.nativ.JLineNativeLoader"); - for (Map.Entry cmdEntry : MVN4_MAIN_CLASSES.entrySet()) { - Class cmdClass = cliClass.getClassLoader().loadClass(cmdEntry.getValue()); - Method mainMethod = cmdClass.getMethod( - "main", - String[].class, - classWorld.getClass(), - InputStream.class, - OutputStream.class, - OutputStream.class); - commands.put(cmdEntry.getKey(), r -> { - System.setProperties(prepareProperties(r)); - try { - ArrayList args = new ArrayList<>(mavenArgs); - args.addAll(r.arguments()); - return (int) mainMethod.invoke( - null, - args.toArray(new String[0]), - classWorld, - r.stdIn().orElse(null), - r.stdOut().orElse(null), - r.stdErr().orElse(null)); - } catch (Exception e) { - throw new ExecutorException("Failed to execute", e); - } - }); - } - } - - return new Context( - bootClassLoader, - version, - classWorld, - originalClassRealmIds, - cliClass.getClassLoader(), - commands, - keepAlive); - } catch (Exception e) { - throw new ExecutorException("Failed to create executor", e); - } finally { - Thread.currentThread().setContextClassLoader(originalClassLoader); - System.setProperties(originalProperties); - } - } - - private boolean mayAddToKeepAlive(List keepAlive, Class cliClass, String className) { - try { - keepAlive.add(cliClass.getClassLoader().loadClass(className)); - return true; - } catch (ClassNotFoundException e) { - return false; - } - } - - protected Properties prepareProperties(ExecutorRequest request) { - System.setProperties(null); // this "inits" them! - - Properties properties = new Properties(); - properties.putAll(System.getProperties()); // get mandatory/expected init-ed above - - properties.setProperty("user.dir", request.cwd().toString()); - properties.setProperty("user.home", request.userHomeDirectory().toString()); - - Path mavenHome = request.installationDirectory(); - properties.setProperty("maven.home", mavenHome.toString()); - properties.setProperty( - "maven.multiModuleProjectDirectory", request.cwd().toString()); - - // Maven 3.x - properties.setProperty( - "library.jansi.path", mavenHome.resolve("lib/jansi-native").toString()); - - // Maven 4.x - properties.setProperty( - "library.jline.path", mavenHome.resolve("lib/jline-native").toString()); - - if (request.jvmSystemProperties().isPresent()) { - properties.putAll(request.jvmSystemProperties().get()); - } - - return properties; - } - - @Override - public void close() throws ExecutorException { - if (closed.compareAndExchange(false, true)) { - try { - ArrayList exceptions = new ArrayList<>(); - for (Context context : contexts.values()) { - try { - doClose(context); - } catch (Exception e) { - exceptions.add(e); - } - } - if (!exceptions.isEmpty()) { - ExecutorException e = new ExecutorException("Could not close cleanly"); - exceptions.forEach(e::addSuppressed); - throw e; - } - } finally { - System.setProperties(originalProperties); - } - } - } - - protected void doClose(Context context) throws ExecutorException { - Thread.currentThread().setContextClassLoader(context.bootClassLoader); - try { - try { - ((Closeable) context.classWorld).close(); - } finally { - context.bootClassLoader.close(); - } - } catch (Exception e) { - throw new ExecutorException("Failed to close cleanly", e); - } finally { - Thread.currentThread().setContextClassLoader(originalClassLoader); - } - } - - protected void validate(ExecutorRequest executorRequest) throws ExecutorException {} - - protected URLClassLoader createMavenBootClassLoader(Path boot, List extraClasspath) { - ArrayList urls = new ArrayList<>(extraClasspath); - try (Stream stream = Files.list(boot)) { - stream.filter(Files::isRegularFile) - .filter(p -> p.toString().endsWith(".jar")) - .forEach(f -> { - try { - urls.add(f.toUri().toURL()); - } catch (MalformedURLException e) { - throw new ExecutorException("Failed to build classpath: " + f, e); - } - }); - } catch (IOException e) { - throw new ExecutorException("Failed to build classpath: " + e, e); - } - if (urls.isEmpty()) { - throw new IllegalArgumentException("Invalid Maven home directory; boot is empty"); - } - return new URLClassLoader( - urls.toArray(new URL[0]), ClassLoader.getSystemClassLoader().getParent()); - } - - protected String getMavenVersion(Class clazz) throws IOException { - Properties props = new Properties(); - try (InputStream is = clazz.getResourceAsStream("/META-INF/maven/org.apache.maven/maven-core/pom.properties")) { - if (is != null) { - props.load(is); - } - String version = props.getProperty("version"); - if (version != null) { - return version; - } - return UNKNOWN_VERSION; - } - } -} diff --git a/impl/maven-executor/src/main/java/org/apache/maven/cling/executor/forked/ForkedMavenExecutor.java b/impl/maven-executor/src/main/java/org/apache/maven/cling/executor/forked/ForkedMavenExecutor.java deleted file mode 100644 index 4671c7427622..000000000000 --- a/impl/maven-executor/src/main/java/org/apache/maven/cling/executor/forked/ForkedMavenExecutor.java +++ /dev/null @@ -1,226 +0,0 @@ -/* - * 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.cling.executor.forked; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.UncheckedIOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicBoolean; - -import org.apache.maven.cling.executor.Executor; -import org.apache.maven.cling.executor.ExecutorException; -import org.apache.maven.cling.executor.ExecutorRequest; - -import static java.util.Objects.requireNonNull; -import static org.apache.maven.cling.executor.ExecutorRequest.getCanonicalPath; - -/** - * Forked executor implementation, that spawns a subprocess with Maven from the installation directory. Very costly - * but provides the best isolation. - */ -public class ForkedMavenExecutor implements Executor { - protected final boolean useMavenArgsEnv; - protected final AtomicBoolean closed; - - public ForkedMavenExecutor() { - this(true); - } - - public ForkedMavenExecutor(boolean useMavenArgsEnv) { - this.useMavenArgsEnv = useMavenArgsEnv; - this.closed = new AtomicBoolean(false); - } - - @Override - public int execute(ExecutorRequest executorRequest) throws ExecutorException { - requireNonNull(executorRequest); - if (closed.get()) { - throw new ExecutorException("Executor is closed"); - } - validate(executorRequest); - - return doExecute(executorRequest); - } - - @Override - public String mavenVersion(ExecutorRequest executorRequest) throws ExecutorException { - requireNonNull(executorRequest); - if (closed.get()) { - throw new ExecutorException("Executor is closed"); - } - validate(executorRequest); - try { - Path cwd = Files.createTempDirectory("forked-executor-maven-version"); - try { - ByteArrayOutputStream stdout = new ByteArrayOutputStream(); - int exitCode = execute(executorRequest.toBuilder() - .cwd(cwd) - .arguments(List.of("--version", "--quiet")) - .stdOut(stdout) - .build()); - if (exitCode == 0) { - if (stdout.size() > 0) { - return stdout.toString() - .replace("\n", "") - .replace("\r", "") - .trim(); - } - return UNKNOWN_VERSION; - } else { - throw new ExecutorException( - "Maven version query unexpected exitCode=" + exitCode + "\nLog: " + stdout); - } - } finally { - Files.deleteIfExists(cwd); - } - } catch (IOException e) { - throw new ExecutorException("Failed to determine maven version", e); - } - } - - protected void validate(ExecutorRequest executorRequest) throws ExecutorException {} - - protected int doExecute(ExecutorRequest executorRequest) throws ExecutorException { - ArrayList cmdAndArguments = new ArrayList<>(); - cmdAndArguments.add(executorRequest - .installationDirectory() - .resolve("bin") - .resolve(IS_WINDOWS ? executorRequest.command() + ".cmd" : executorRequest.command()) - .toString()); - - String mavenArgsEnv = System.getenv("MAVEN_ARGS"); - if (useMavenArgsEnv && mavenArgsEnv != null && !mavenArgsEnv.isEmpty()) { - Arrays.stream(mavenArgsEnv.split(" ")) - .filter(s -> !s.trim().isEmpty()) - .forEach(cmdAndArguments::add); - } - - cmdAndArguments.addAll(executorRequest.arguments()); - - ArrayList jvmArgs = new ArrayList<>(); - if (!executorRequest.userHomeDirectory().equals(getCanonicalPath(Paths.get(System.getProperty("user.home"))))) { - jvmArgs.add("-Duser.home=" + executorRequest.userHomeDirectory().toString()); - } - if (executorRequest.jvmArguments().isPresent()) { - jvmArgs.addAll(executorRequest.jvmArguments().get()); - } - if (executorRequest.jvmSystemProperties().isPresent()) { - jvmArgs.addAll(executorRequest.jvmSystemProperties().get().entrySet().stream() - .map(e -> "-D" + e.getKey() + "=" + e.getValue()) - .toList()); - } - - HashMap env = new HashMap<>(); - if (executorRequest.environmentVariables().isPresent()) { - env.putAll(executorRequest.environmentVariables().get()); - } - if (!jvmArgs.isEmpty()) { - String mavenOpts = env.getOrDefault("MAVEN_OPTS", ""); - if (!mavenOpts.isEmpty()) { - mavenOpts += " "; - } - mavenOpts += String.join(" ", jvmArgs); - env.put("MAVEN_OPTS", mavenOpts); - } - env.remove("MAVEN_ARGS"); // we already used it if configured to do so - - if (executorRequest.skipMavenRc()) { - env.put("MAVEN_SKIP_RC", "true"); - } - - try { - ProcessBuilder pb = new ProcessBuilder() - .directory(executorRequest.cwd().toFile()) - .command(cmdAndArguments); - if (!env.isEmpty()) { - pb.environment().putAll(env); - } - - Process process = pb.start(); - pump(process, executorRequest).await(); - return process.waitFor(); - } catch (IOException e) { - throw new ExecutorException("IO problem while executing command: " + cmdAndArguments, e); - } catch (InterruptedException e) { - throw new ExecutorException("Interrupted while executing command: " + cmdAndArguments, e); - } - } - - protected CountDownLatch pump(Process p, ExecutorRequest executorRequest) { - CountDownLatch latch = new CountDownLatch(3); - String suffix = "-pump-" + p.pid(); - Thread stdoutPump = new Thread(() -> { - try { - OutputStream stdout = executorRequest.stdOut().orElse(OutputStream.nullOutputStream()); - p.getInputStream().transferTo(stdout); - stdout.flush(); - } catch (IOException e) { - throw new UncheckedIOException(e); - } finally { - latch.countDown(); - } - }); - stdoutPump.setName("stdout" + suffix); - stdoutPump.start(); - Thread stderrPump = new Thread(() -> { - try { - OutputStream stderr = executorRequest.stdErr().orElse(OutputStream.nullOutputStream()); - p.getErrorStream().transferTo(stderr); - stderr.flush(); - } catch (IOException e) { - throw new UncheckedIOException(e); - } finally { - latch.countDown(); - } - }); - stderrPump.setName("stderr" + suffix); - stderrPump.start(); - Thread stdinPump = new Thread(() -> { - try { - OutputStream in = p.getOutputStream(); - executorRequest.stdIn().orElse(InputStream.nullInputStream()).transferTo(in); - in.flush(); - } catch (IOException e) { - throw new UncheckedIOException(e); - } finally { - latch.countDown(); - } - }); - stdinPump.setName("stdin" + suffix); - stdinPump.start(); - return latch; - } - - @Override - public void close() throws ExecutorException { - if (closed.compareAndExchange(false, true)) { - // nothing yet - } - } -} diff --git a/impl/maven-executor/src/main/java/org/apache/maven/cling/executor/internal/HelperImpl.java b/impl/maven-executor/src/main/java/org/apache/maven/cling/executor/internal/HelperImpl.java deleted file mode 100644 index d8109ba3e252..000000000000 --- a/impl/maven-executor/src/main/java/org/apache/maven/cling/executor/internal/HelperImpl.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * 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.cling.executor.internal; - -import java.nio.file.Path; -import java.util.HashMap; -import java.util.concurrent.ConcurrentHashMap; - -import org.apache.maven.api.annotations.Nullable; -import org.apache.maven.cling.executor.Executor; -import org.apache.maven.cling.executor.ExecutorException; -import org.apache.maven.cling.executor.ExecutorHelper; -import org.apache.maven.cling.executor.ExecutorRequest; - -import static java.util.Objects.requireNonNull; - -/** - * Simple router to executors, and delegate to executor tool. - */ -public class HelperImpl implements ExecutorHelper { - private final Mode defaultMode; - private final Path installationDirectory; - private final Path userHomeDirectory; - private final HashMap executors; - - private final ConcurrentHashMap cache; - - public HelperImpl( - Mode defaultMode, - @Nullable Path installationDirectory, - @Nullable Path userHomeDirectory, - Executor embedded, - Executor forked) { - this.defaultMode = requireNonNull(defaultMode); - this.installationDirectory = installationDirectory != null - ? ExecutorRequest.getCanonicalPath(installationDirectory) - : ExecutorRequest.discoverInstallationDirectory(); - this.userHomeDirectory = userHomeDirectory != null - ? ExecutorRequest.getCanonicalPath(userHomeDirectory) - : ExecutorRequest.discoverUserHomeDirectory(); - this.executors = new HashMap<>(); - - this.executors.put(Mode.EMBEDDED, requireNonNull(embedded, "embedded")); - this.executors.put(Mode.FORKED, requireNonNull(forked, "forked")); - this.cache = new ConcurrentHashMap<>(); - } - - @Override - public Mode getDefaultMode() { - return defaultMode; - } - - @Override - public ExecutorRequest.Builder executorRequest() { - return ExecutorRequest.mavenBuilder(installationDirectory).userHomeDirectory(userHomeDirectory); - } - - @Override - public int execute(Mode mode, ExecutorRequest executorRequest) throws ExecutorException { - return getExecutor(mode, executorRequest).execute(executorRequest); - } - - @Override - public String mavenVersion() { - return cache.computeIfAbsent("maven.version", k -> { - ExecutorRequest request = executorRequest().build(); - return getExecutor(Mode.AUTO, request).mavenVersion(request); - }); - } - - protected Executor getExecutor(Mode mode, ExecutorRequest request) throws ExecutorException { - return switch (mode) { - case AUTO -> getExecutorByRequest(request); - case EMBEDDED -> executors.get(Mode.EMBEDDED); - case FORKED -> executors.get(Mode.FORKED); - }; - } - - private Executor getExecutorByRequest(ExecutorRequest request) { - if (request.environmentVariables().isEmpty() && request.jvmArguments().isEmpty()) { - return getExecutor(Mode.EMBEDDED, request); - } else { - return getExecutor(Mode.FORKED, request); - } - } -} diff --git a/impl/maven-executor/src/main/java/org/apache/maven/cling/executor/internal/ToolboxTool.java b/impl/maven-executor/src/main/java/org/apache/maven/cling/executor/internal/ToolboxTool.java deleted file mode 100644 index 2adc21c43819..000000000000 --- a/impl/maven-executor/src/main/java/org/apache/maven/cling/executor/internal/ToolboxTool.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * 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.cling.executor.internal; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; -import java.util.Properties; -import java.util.stream.Collectors; - -import org.apache.maven.cling.executor.ExecutorException; -import org.apache.maven.cling.executor.ExecutorHelper; -import org.apache.maven.cling.executor.ExecutorRequest; -import org.apache.maven.cling.executor.ExecutorTool; - -import static java.util.Objects.requireNonNull; - -/** - * {@link ExecutorTool} implementation based on Maveniverse Toolbox. It uses Toolbox mojos to implement all the - * required operations. - * - * @see Maveniverse Toolbox - */ -public class ToolboxTool implements ExecutorTool { - private static final String TOOLBOX_PREFIX = "eu.maveniverse.maven.plugins:toolbox:"; - - private final ExecutorHelper helper; - private final String toolboxVersion; - private final ExecutorHelper.Mode forceMode; - - /** - * @deprecated Better specify required version yourself. This one is "cemented" to 0.13.7 - */ - @Deprecated - public ToolboxTool(ExecutorHelper helper) { - this(helper, "0.13.7"); - } - - public ToolboxTool(ExecutorHelper helper, String toolboxVersion) { - this(helper, toolboxVersion, null); - } - - public ToolboxTool(ExecutorHelper helper, String toolboxVersion, ExecutorHelper.Mode forceMode) { - this.helper = requireNonNull(helper); - this.toolboxVersion = requireNonNull(toolboxVersion); - this.forceMode = forceMode; // nullable - } - - @Override - public Map dump(ExecutorRequest.Builder executorRequest) throws ExecutorException { - ByteArrayOutputStream stdout = new ByteArrayOutputStream(); - ByteArrayOutputStream stderr = new ByteArrayOutputStream(); - ExecutorRequest.Builder builder = mojo(executorRequest, "gav-dump") - .argument("-DasProperties") - .stdOut(stdout) - .stdErr(stderr); - doExecute(builder); - try { - Properties properties = new Properties(); - properties.load(new ByteArrayInputStream( - validateOutput(false, stdout, stderr).getBytes())); - return properties.entrySet().stream() - .collect(Collectors.toMap( - e -> String.valueOf(e.getKey()), - e -> String.valueOf(e.getValue()), - (prev, next) -> next, - HashMap::new)); - } catch (IOException e) { - throw new ExecutorException("Unable to parse properties", e); - } - } - - @Override - public String localRepository(ExecutorRequest.Builder executorRequest) throws ExecutorException { - ByteArrayOutputStream stdout = new ByteArrayOutputStream(); - ByteArrayOutputStream stderr = new ByteArrayOutputStream(); - ExecutorRequest.Builder builder = mojo(executorRequest, "gav-local-repository-path") - .stdOut(stdout) - .stdErr(stderr); - doExecute(builder); - return validateOutput(true, stdout, stderr); - } - - @Override - public String artifactPath(ExecutorRequest.Builder executorRequest, String gav, String repositoryId) - throws ExecutorException { - ByteArrayOutputStream stdout = new ByteArrayOutputStream(); - ByteArrayOutputStream stderr = new ByteArrayOutputStream(); - ExecutorRequest.Builder builder = mojo(executorRequest, "gav-artifact-path") - .argument("-Dgav=" + gav) - .stdOut(stdout) - .stdErr(stderr); - if (repositoryId != null) { - builder.argument("-Drepository=" + repositoryId + "::unimportant"); - } - doExecute(builder); - return validateOutput(true, stdout, stderr); - } - - @Override - public String metadataPath(ExecutorRequest.Builder executorRequest, String gav, String repositoryId) - throws ExecutorException { - ByteArrayOutputStream stdout = new ByteArrayOutputStream(); - ByteArrayOutputStream stderr = new ByteArrayOutputStream(); - ExecutorRequest.Builder builder = mojo(executorRequest, "gav-metadata-path") - .argument("-Dgav=" + gav) - .stdOut(stdout) - .stdErr(stderr); - if (repositoryId != null) { - builder.argument("-Drepository=" + repositoryId + "::unimportant"); - } - doExecute(builder); - return validateOutput(true, stdout, stderr); - } - - private ExecutorRequest.Builder mojo(ExecutorRequest.Builder builder, String mojo) { - if (helper.mavenVersion().startsWith("4.")) { - builder.argument("--raw-streams"); - } - return builder.argument(TOOLBOX_PREFIX + toolboxVersion + ":" + mojo) - .argument("--quiet") - .argument("-DforceStdout"); - } - - private void doExecute(ExecutorRequest.Builder builder) { - ExecutorRequest request = builder.build(); - int ec = forceMode == null ? helper.execute(request) : helper.execute(forceMode, request); - if (ec != 0) { - throw new ExecutorException("Unexpected exit code=" + ec + "; stdout=" - + request.stdOut().orElse(null) + "; stderr=" - + request.stdErr().orElse(null)); - } - } - - /** - * Performs "sanity check" for output, making sure no insane values like empty strings are returned. - */ - private String validateOutput(boolean shave, ByteArrayOutputStream stdout, ByteArrayOutputStream stderr) { - String result = stdout.toString(); - if (shave) { - result = result.replace("\n", "").replace("\r", ""); - } - // sanity checks: stderr has any OR result is empty string (no method should emit empty string) - if (result.trim().isEmpty()) { - // see bug https://github.com/apache/maven/pull/11303 Fail in this case - // tl;dr We NEVER expect empty string as output from this tool; so fail here instead to chase ghosts - throw new IllegalStateException("Empty output from Toolbox; stdout[" + stdout.size() + "]=" + stdout - + "; stderr[" + stderr.size() + "]=" + stderr); - } - return result; - } -} diff --git a/impl/maven-executor/src/site/site.xml b/impl/maven-executor/src/site/site.xml deleted file mode 100644 index 4ee3b709cfc4..000000000000 --- a/impl/maven-executor/src/site/site.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - ${project.scm.url} - - - - - - - - - - diff --git a/impl/maven-executor/src/test/java/org/apache/maven/cling/executor/Environment.java b/impl/maven-executor/src/test/java/org/apache/maven/cling/executor/Environment.java deleted file mode 100644 index 771661a435a9..000000000000 --- a/impl/maven-executor/src/test/java/org/apache/maven/cling/executor/Environment.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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.cling.executor; - -public final class Environment { - private Environment() {} - - public static final String TOOLBOX_VERSION = System.getProperty("version.toolbox", "UNSET version.toolbox"); -} diff --git a/impl/maven-executor/src/test/java/org/apache/maven/cling/executor/MavenExecutorTestSupport.java b/impl/maven-executor/src/test/java/org/apache/maven/cling/executor/MavenExecutorTestSupport.java deleted file mode 100644 index a2a095271cf6..000000000000 --- a/impl/maven-executor/src/test/java/org/apache/maven/cling/executor/MavenExecutorTestSupport.java +++ /dev/null @@ -1,412 +0,0 @@ -/* - * 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.cling.executor; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Collection; -import java.util.List; - -import eu.maveniverse.maven.mimir.testing.MimirInfuser; -import org.apache.maven.api.annotations.Nullable; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInfo; -import org.junit.jupiter.api.Timeout; -import org.junit.jupiter.api.condition.DisabledOnOs; -import org.junit.jupiter.api.io.CleanupMode; -import org.junit.jupiter.api.io.TempDir; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.condition.OS.WINDOWS; - -@Timeout(60) -public abstract class MavenExecutorTestSupport { - @TempDir(cleanup = CleanupMode.NEVER) - private static Path tempDir; - - private Path cwd; - - private Path userHome; - - @BeforeEach - void beforeEach(TestInfo testInfo) throws Exception { - cwd = tempDir.resolve(testInfo.getTestMethod().orElseThrow().getName()).resolve("cwd"); - Files.createDirectories(cwd.resolve(".mvn")); - userHome = tempDir.resolve(testInfo.getTestMethod().orElseThrow().getName()) - .resolve("home"); - Files.createDirectories(userHome); - - System.out.println("=== " + testInfo.getTestMethod().orElseThrow().getName()); - } - - private static Executor executor; - - protected final Executor createAndMemoizeExecutor() { - if (executor == null) { - executor = doSelectExecutor(); - } - return executor; - } - - @AfterAll - static void afterAll() { - if (executor != null) { - executor.close(); - executor = null; - } - } - - protected abstract Executor doSelectExecutor(); - - @Test - void mvnenc4() throws Exception { - String logfile = "m4.log"; - execute( - cwd.resolve(logfile), - List.of(mvn4ExecutorRequestBuilder() - .command("mvnenc") - .cwd(cwd) - .userHomeDirectory(userHome) - .argument("diag") - .argument("-l") - .argument(logfile) - .build())); - System.out.println(Files.readString(cwd.resolve(logfile))); - } - - @DisabledOnOs( - value = WINDOWS, - disabledReason = "JUnit on Windows fails to clean up as mvn3 does not close log file properly") - @Test - void dump3() throws Exception { - String logfile = "m3.log"; - execute( - cwd.resolve(logfile), - List.of(mvn3ExecutorRequestBuilder() - .cwd(cwd) - .userHomeDirectory(userHome) - .argument("eu.maveniverse.maven.plugins:toolbox:" + Environment.TOOLBOX_VERSION + ":gav-dump") - .argument("-l") - .argument(logfile) - .build())); - System.out.println(Files.readString(cwd.resolve(logfile))); - } - - @Test - void dump4() throws Exception { - String logfile = "m4.log"; - execute( - cwd.resolve(logfile), - List.of(mvn4ExecutorRequestBuilder() - .cwd(cwd) - .userHomeDirectory(userHome) - .argument("eu.maveniverse.maven.plugins:toolbox:" + Environment.TOOLBOX_VERSION + ":gav-dump") - .argument("-l") - .argument(logfile) - .build())); - System.out.println(Files.readString(cwd.resolve(logfile))); - } - - @DisabledOnOs( - value = WINDOWS, - disabledReason = "JUnit on Windows fails to clean up as mvn3 does not close log file properly") - @Test - void defaultFs3() throws Exception { - layDownFiles(cwd); - String logfile = "m3.log"; - execute( - cwd.resolve(logfile), - List.of(mvn3ExecutorRequestBuilder() - .cwd(cwd) - .argument("-V") - .argument("verify") - .argument("-l") - .argument(logfile) - .build())); - System.out.println(Files.readString(cwd.resolve(logfile))); - } - - @Test - void defaultFs4() throws Exception { - layDownFiles(cwd); - String logfile = "m4.log"; - execute( - cwd.resolve(logfile), - List.of(mvn4ExecutorRequestBuilder() - .cwd(cwd) - .argument("-V") - .argument("verify") - .argument("-l") - .argument(logfile) - .build())); - System.out.println(Files.readString(cwd.resolve(logfile))); - } - - @Test - void version3() throws Exception { - assertEquals( - System.getProperty("maven3version"), - mavenVersion(mvn3ExecutorRequestBuilder().build())); - } - - @Test - void version4() throws Exception { - assertEquals( - System.getProperty("maven4version"), - mavenVersion(mvn4ExecutorRequestBuilder().build())); - } - - @Test - void defaultFs4CaptureOutput() throws Exception { - layDownFiles(cwd); - ByteArrayOutputStream stdout = new ByteArrayOutputStream(); - execute( - null, - List.of(mvn4ExecutorRequestBuilder() - .cwd(cwd) - .argument("-V") - .argument("verify") - .stdOut(stdout) - .build())); - System.out.println(stdout); - assertFalse(stdout.toString().contains("[\u001B["), "By default no ANSI color codes"); - assertTrue(stdout.toString().contains("INFO"), "No INFO found"); - } - - @Test - void defaultFs4CaptureOutputWithForcedColor() throws Exception { - layDownFiles(cwd); - ByteArrayOutputStream stdout = new ByteArrayOutputStream(); - execute( - null, - List.of(mvn4ExecutorRequestBuilder() - .cwd(cwd) - .argument("-V") - .argument("verify") - .argument("--color=yes") - .stdOut(stdout) - .build())); - System.out.println(stdout); - assertTrue(stdout.toString().contains("[\u001B["), "No ANSI codes present"); - assertTrue(stdout.toString().contains("INFO"), "No INFO found"); - } - - @Test - void defaultFs4CaptureOutputWithForcedOffColor() throws Exception { - layDownFiles(cwd); - ByteArrayOutputStream stdout = new ByteArrayOutputStream(); - execute( - null, - List.of(mvn4ExecutorRequestBuilder() - .cwd(cwd) - .argument("-V") - .argument("verify") - .argument("--color=no") - .stdOut(stdout) - .build())); - System.out.println(stdout); - assertFalse(stdout.toString().contains("[\u001B["), "No ANSI codes present"); - assertTrue(stdout.toString().contains("INFO"), "No INFO found"); - } - - @Test - void defaultFs3CaptureOutput() throws Exception { - layDownFiles(cwd); - ByteArrayOutputStream stdout = new ByteArrayOutputStream(); - execute( - null, - List.of(mvn3ExecutorRequestBuilder() - .cwd(cwd) - .argument("-V") - .argument("verify") - .stdOut(stdout) - .build())); - System.out.println(stdout); - // Note: we do not validate ANSI as Maven3 is weird in this respect (thinks is color but is not) - // assertTrue(stdout.toString().contains("[\u001B["), "No ANSI codes present"); - assertTrue(stdout.toString().contains("INFO"), "No INFO found"); - } - - @Test - void defaultFs3CaptureOutputWithForcedColor() throws Exception { - layDownFiles(cwd); - ByteArrayOutputStream stdout = new ByteArrayOutputStream(); - execute( - null, - List.of(mvn3ExecutorRequestBuilder() - .cwd(cwd) - .argument("-V") - .argument("verify") - .argument("--color=yes") - .stdOut(stdout) - .build())); - System.out.println(stdout); - assertTrue(stdout.toString().contains("[\u001B["), "No ANSI codes present"); - assertTrue(stdout.toString().contains("INFO"), "No INFO found"); - } - - @Test - void defaultFs3CaptureOutputWithForcedOffColor() throws Exception { - layDownFiles(cwd); - ByteArrayOutputStream stdout = new ByteArrayOutputStream(); - execute( - null, - List.of(mvn3ExecutorRequestBuilder() - .cwd(cwd) - .argument("-V") - .argument("verify") - .argument("--color=no") - .stdOut(stdout) - .build())); - System.out.println(stdout); - assertFalse(stdout.toString().contains("[\u001B["), "No ANSI codes present"); - assertTrue(stdout.toString().contains("INFO"), "No INFO found"); - } - - public static final String POM_STRING = """ - - - - 4.0.0 - - org.apache.maven.samples - sample - 1.0.0 - - - - - org.junit - junit-bom - 5.11.1 - pom - import - - - - - - - org.junit.jupiter - junit-jupiter-api - test - - - - - """; - - public static final String APP_JAVA_STRING = """ - package org.apache.maven.samples.sample; - - public class App { - public static void main(String... args) { - System.out.println("Hello World!"); - } - } - """; - - protected void execute(@Nullable Path logFile, Collection requests) throws Exception { - Executor invoker = createAndMemoizeExecutor(); - for (ExecutorRequest request : requests) { - if (MimirInfuser.isMimirPresentUW()) { - if (maven3Home().equals(request.installationDirectory())) { - MimirInfuser.doInfusePW(request.cwd(), request.userHomeDirectory()); - } else if (maven4Home().equals(request.installationDirectory())) { - MimirInfuser.doInfuseUW(request.userHomeDirectory()); - } - MimirInfuser.preseedItselfIntoInnerUserHome(request.userHomeDirectory()); - } - int exitCode = invoker.execute(request); - if (exitCode != 0) { - throw new FailedExecution(request, exitCode, logFile == null ? "" : Files.readString(logFile)); - } - } - } - - protected String mavenVersion(ExecutorRequest request) throws Exception { - return createAndMemoizeExecutor().mavenVersion(request); - } - - public ExecutorRequest.Builder mvn3ExecutorRequestBuilder() { - return customize(ExecutorRequest.mavenBuilder(maven3Home())); - } - - private Path maven3Home() { - return ExecutorRequest.getCanonicalPath(Paths.get(System.getProperty("maven3home"))); - } - - public ExecutorRequest.Builder mvn4ExecutorRequestBuilder() { - return customize(ExecutorRequest.mavenBuilder(maven4Home())); - } - - private Path maven4Home() { - return ExecutorRequest.getCanonicalPath(Paths.get(System.getProperty("maven4home"))); - } - - private ExecutorRequest.Builder customize(ExecutorRequest.Builder builder) { - builder = - builder.cwd(cwd).userHomeDirectory(userHome).argument("-Daether.remoteRepositoryFilter.prefixes=false"); - if (System.getProperty("localRepository") != null) { - builder.argument("-Dmaven.repo.local.tail=" + System.getProperty("localRepository")); - } - return builder; - } - - protected void layDownFiles(Path cwd) throws IOException { - Path pom = cwd.resolve("pom.xml").toAbsolutePath(); - Files.writeString(pom, POM_STRING); - Path appJava = cwd.resolve("src/main/java/org/apache/maven/samples/sample/App.java"); - Files.createDirectories(appJava.getParent()); - Files.writeString(appJava, APP_JAVA_STRING); - } - - protected static class FailedExecution extends Exception { - private final ExecutorRequest request; - private final int exitCode; - private final String log; - - public FailedExecution(ExecutorRequest request, int exitCode, String log) { - super(request.toString() + " => " + exitCode + "\n" + log); - this.request = request; - this.exitCode = exitCode; - this.log = log; - } - - public ExecutorRequest getRequest() { - return request; - } - - public int getExitCode() { - return exitCode; - } - - public String getLog() { - return log; - } - } -} diff --git a/impl/maven-executor/src/test/java/org/apache/maven/cling/executor/embedded/EmbeddedMavenExecutorTest.java b/impl/maven-executor/src/test/java/org/apache/maven/cling/executor/embedded/EmbeddedMavenExecutorTest.java deleted file mode 100644 index 6433235b6927..000000000000 --- a/impl/maven-executor/src/test/java/org/apache/maven/cling/executor/embedded/EmbeddedMavenExecutorTest.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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.cling.executor.embedded; - -import org.apache.maven.cling.executor.Executor; -import org.apache.maven.cling.executor.MavenExecutorTestSupport; - -/** - * Embedded executor UT - */ -public class EmbeddedMavenExecutorTest extends MavenExecutorTestSupport { - - @Override - protected Executor doSelectExecutor() { - return new EmbeddedMavenExecutor(); - } -} diff --git a/impl/maven-executor/src/test/java/org/apache/maven/cling/executor/forked/ForkedMavenExecutorTest.java b/impl/maven-executor/src/test/java/org/apache/maven/cling/executor/forked/ForkedMavenExecutorTest.java deleted file mode 100644 index a1f4362eac9b..000000000000 --- a/impl/maven-executor/src/test/java/org/apache/maven/cling/executor/forked/ForkedMavenExecutorTest.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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.cling.executor.forked; - -import org.apache.maven.cling.executor.Executor; -import org.apache.maven.cling.executor.MavenExecutorTestSupport; - -/** - * Forked executor UT - */ -public class ForkedMavenExecutorTest extends MavenExecutorTestSupport { - - @Override - protected Executor doSelectExecutor() { - return new ForkedMavenExecutor(); - } -} diff --git a/impl/maven-executor/src/test/java/org/apache/maven/cling/executor/impl/ToolboxToolTest.java b/impl/maven-executor/src/test/java/org/apache/maven/cling/executor/impl/ToolboxToolTest.java deleted file mode 100644 index fc856268248c..000000000000 --- a/impl/maven-executor/src/test/java/org/apache/maven/cling/executor/impl/ToolboxToolTest.java +++ /dev/null @@ -1,211 +0,0 @@ -/* - * 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.cling.executor.impl; - -import java.io.File; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Map; - -import eu.maveniverse.maven.mimir.testing.MimirInfuser; -import org.apache.maven.cling.executor.Environment; -import org.apache.maven.cling.executor.Executor; -import org.apache.maven.cling.executor.ExecutorHelper; -import org.apache.maven.cling.executor.ExecutorRequest; -import org.apache.maven.cling.executor.embedded.EmbeddedMavenExecutor; -import org.apache.maven.cling.executor.forked.ForkedMavenExecutor; -import org.apache.maven.cling.executor.internal.HelperImpl; -import org.apache.maven.cling.executor.internal.ToolboxTool; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.TestInfo; -import org.junit.jupiter.api.Timeout; -import org.junit.jupiter.api.io.CleanupMode; -import org.junit.jupiter.api.io.TempDir; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.EnumSource; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -@Timeout(60) -public class ToolboxToolTest { - private static final Executor EMBEDDED_MAVEN_EXECUTOR = new EmbeddedMavenExecutor(); - private static final Executor FORKED_MAVEN_EXECUTOR = new ForkedMavenExecutor(); - - @TempDir(cleanup = CleanupMode.NEVER) - private static Path tempDir; - - private Path userHome; - private Path cwd; - - @BeforeEach - void beforeEach(TestInfo testInfo) throws Exception { - String testName = testInfo.getTestMethod().orElseThrow().getName(); - userHome = tempDir.resolve(testName); - cwd = userHome.resolve("cwd"); - Files.createDirectories(cwd.resolve(".mvn")); - - if (MimirInfuser.isMimirPresentUW()) { - if (testName.contains("3")) { - MimirInfuser.doInfusePW(cwd, userHome); - } else { - MimirInfuser.doInfuseUW(userHome); - } - MimirInfuser.preseedItselfIntoInnerUserHome(userHome); - } - - System.out.println("=== " + testInfo.getTestMethod().orElseThrow().getName()); - } - - private ExecutorRequest.Builder getExecutorRequest(ExecutorHelper helper) { - ExecutorRequest.Builder builder = - helper.executorRequest().cwd(cwd).argument("-Daether.remoteRepositoryFilter.prefixes=false"); - if (System.getProperty("localRepository") != null) { - builder.argument("-Dmaven.repo.local.tail=" + System.getProperty("localRepository")); - } - return builder; - } - - @ParameterizedTest - @EnumSource(ExecutorHelper.Mode.class) - void dump3(ExecutorHelper.Mode mode) throws Exception { - ExecutorHelper helper = - new HelperImpl(mode, mvn3Home(), userHome, EMBEDDED_MAVEN_EXECUTOR, FORKED_MAVEN_EXECUTOR); - Map dump = - new ToolboxTool(helper, Environment.TOOLBOX_VERSION).dump(getExecutorRequest(helper)); - System.out.println(mode.name() + ": " + dump.toString()); - assertEquals(System.getProperty("maven3version"), dump.get("maven.version")); - } - - @ParameterizedTest - @EnumSource(ExecutorHelper.Mode.class) - void dump4(ExecutorHelper.Mode mode) throws Exception { - ExecutorHelper helper = - new HelperImpl(mode, mvn4Home(), userHome, EMBEDDED_MAVEN_EXECUTOR, FORKED_MAVEN_EXECUTOR); - Map dump = - new ToolboxTool(helper, Environment.TOOLBOX_VERSION).dump(getExecutorRequest(helper)); - System.out.println(mode.name() + ": " + dump.toString()); - assertEquals(System.getProperty("maven4version"), dump.get("maven.version")); - } - - @ParameterizedTest - @EnumSource(ExecutorHelper.Mode.class) - void version3(ExecutorHelper.Mode mode) { - ExecutorHelper helper = - new HelperImpl(mode, mvn3Home(), userHome, EMBEDDED_MAVEN_EXECUTOR, FORKED_MAVEN_EXECUTOR); - System.out.println(mode.name() + ": " + helper.mavenVersion()); - assertEquals(System.getProperty("maven3version"), helper.mavenVersion()); - } - - @ParameterizedTest - @EnumSource(ExecutorHelper.Mode.class) - void version4(ExecutorHelper.Mode mode) { - ExecutorHelper helper = - new HelperImpl(mode, mvn4Home(), userHome, EMBEDDED_MAVEN_EXECUTOR, FORKED_MAVEN_EXECUTOR); - System.out.println(mode.name() + ": " + helper.mavenVersion()); - assertEquals(System.getProperty("maven4version"), helper.mavenVersion()); - } - - @ParameterizedTest - @EnumSource(ExecutorHelper.Mode.class) - void localRepository3(ExecutorHelper.Mode mode) { - ExecutorHelper helper = - new HelperImpl(mode, mvn3Home(), userHome, EMBEDDED_MAVEN_EXECUTOR, FORKED_MAVEN_EXECUTOR); - String localRepository = - new ToolboxTool(helper, Environment.TOOLBOX_VERSION).localRepository(getExecutorRequest(helper)); - System.out.println(mode.name() + ": " + localRepository); - Path local = Paths.get(localRepository); - assertTrue(Files.isDirectory(local)); - } - - @ParameterizedTest - @EnumSource(ExecutorHelper.Mode.class) - void localRepository4(ExecutorHelper.Mode mode) { - ExecutorHelper helper = - new HelperImpl(mode, mvn4Home(), userHome, EMBEDDED_MAVEN_EXECUTOR, FORKED_MAVEN_EXECUTOR); - String localRepository = - new ToolboxTool(helper, Environment.TOOLBOX_VERSION).localRepository(getExecutorRequest(helper)); - System.out.println(mode.name() + ": " + localRepository); - Path local = Paths.get(localRepository); - assertTrue(Files.isDirectory(local)); - } - - @ParameterizedTest - @EnumSource(ExecutorHelper.Mode.class) - void artifactPath3(ExecutorHelper.Mode mode) { - ExecutorHelper helper = - new HelperImpl(mode, mvn3Home(), userHome, EMBEDDED_MAVEN_EXECUTOR, FORKED_MAVEN_EXECUTOR); - String path = new ToolboxTool(helper, Environment.TOOLBOX_VERSION) - .artifactPath(getExecutorRequest(helper), "aopalliance:aopalliance:1.0", "central"); - System.out.println(mode.name() + ": " + path); - // split repository: assert "ends with" as split may introduce prefixes - assertTrue( - path.endsWith("aopalliance" + File.separator + "aopalliance" + File.separator + "1.0" + File.separator - + "aopalliance-1.0.jar"), - "path=" + path); - } - - @ParameterizedTest - @EnumSource(ExecutorHelper.Mode.class) - void artifactPath4(ExecutorHelper.Mode mode) { - ExecutorHelper helper = - new HelperImpl(mode, mvn4Home(), userHome, EMBEDDED_MAVEN_EXECUTOR, FORKED_MAVEN_EXECUTOR); - String path = new ToolboxTool(helper, Environment.TOOLBOX_VERSION) - .artifactPath(getExecutorRequest(helper), "aopalliance:aopalliance:1.0", "central"); - System.out.println(mode.name() + ": " + path); - // split repository: assert "ends with" as split may introduce prefixes - assertTrue( - path.endsWith("aopalliance" + File.separator + "aopalliance" + File.separator + "1.0" + File.separator - + "aopalliance-1.0.jar"), - "path=" + path); - } - - @ParameterizedTest - @EnumSource(ExecutorHelper.Mode.class) - void metadataPath3(ExecutorHelper.Mode mode) { - ExecutorHelper helper = - new HelperImpl(mode, mvn4Home(), userHome, EMBEDDED_MAVEN_EXECUTOR, FORKED_MAVEN_EXECUTOR); - String path = new ToolboxTool(helper, Environment.TOOLBOX_VERSION) - .metadataPath(getExecutorRequest(helper), "aopalliance", "someremote"); - System.out.println(mode.name() + ": " + path); - // split repository: assert "ends with" as split may introduce prefixes - assertTrue(path.endsWith("aopalliance" + File.separator + "maven-metadata-someremote.xml"), "path=" + path); - } - - @ParameterizedTest - @EnumSource(ExecutorHelper.Mode.class) - void metadataPath4(ExecutorHelper.Mode mode) { - ExecutorHelper helper = - new HelperImpl(mode, mvn4Home(), userHome, EMBEDDED_MAVEN_EXECUTOR, FORKED_MAVEN_EXECUTOR); - String path = new ToolboxTool(helper, Environment.TOOLBOX_VERSION) - .metadataPath(getExecutorRequest(helper), "aopalliance", "someremote"); - System.out.println(mode.name() + ": " + path); - // split repository: assert "ends with" as split may introduce prefixes - assertTrue(path.endsWith("aopalliance" + File.separator + "maven-metadata-someremote.xml"), "path=" + path); - } - - public Path mvn3Home() { - return Paths.get(System.getProperty("maven3home")); - } - - public Path mvn4Home() { - return Paths.get(System.getProperty("maven4home")); - } -} diff --git a/impl/pom.xml b/impl/pom.xml index df83a63a018a..b41717c555ea 100644 --- a/impl/pom.xml +++ b/impl/pom.xml @@ -40,7 +40,6 @@ under the License. maven-core maven-cli maven-testing - maven-executor diff --git a/its/core-it-support/maven-it-helper/pom.xml b/its/core-it-support/maven-it-helper/pom.xml index 960eb651e31b..2e9fc52736a9 100644 --- a/its/core-it-support/maven-it-helper/pom.xml +++ b/its/core-it-support/maven-it-helper/pom.xml @@ -32,7 +32,7 @@ under the License. - org.apache.maven + org.apache.maven.executor maven-executor diff --git a/its/core-it-support/maven-it-helper/src/main/java/org/apache/maven/it/Verifier.java b/its/core-it-support/maven-it-helper/src/main/java/org/apache/maven/it/Verifier.java index c0f29a2b3d16..89134a03c671 100644 --- a/its/core-it-support/maven-it-helper/src/main/java/org/apache/maven/it/Verifier.java +++ b/its/core-it-support/maven-it-helper/src/main/java/org/apache/maven/it/Verifier.java @@ -45,14 +45,14 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import org.apache.maven.cling.executor.ExecutorException; -import org.apache.maven.cling.executor.ExecutorRequest; -import org.apache.maven.cling.executor.ExecutorHelper; -import org.apache.maven.cling.executor.ExecutorTool; -import org.apache.maven.cling.executor.embedded.EmbeddedMavenExecutor; -import org.apache.maven.cling.executor.forked.ForkedMavenExecutor; -import org.apache.maven.cling.executor.internal.HelperImpl; -import org.apache.maven.cling.executor.internal.ToolboxTool; +import org.apache.maven.executor.Executor; +import org.apache.maven.executor.ExecutorException; +import org.apache.maven.executor.ExecutorRequest; +import org.apache.maven.executor.ExecutorHelper; +import org.apache.maven.executor.ExecutorTool; +import org.apache.maven.executor.embedded.EmbeddedMavenExecutor; +import org.apache.maven.executor.forked.ForkedMavenExecutor; +import org.apache.maven.executor.support.ToolboxExecutorTool; import org.codehaus.plexus.util.FileUtils; import org.codehaus.plexus.util.StringUtils; @@ -62,17 +62,21 @@ * */ public class Verifier { + /** + * The Maven home/installation directory we are testing/executing with Executor. + */ + private static final Path MAVEN_HOME = Paths.get(System.getProperty("maven.home")); /** * Keep executor alive, as long as Verifier is in classloader. Embedded classloader keeps embedded Maven * ClassWorld alive, instead to re-create it per invocation, making embedded execution fast(er). + * Points to test subjected Maven home. */ - private static final EmbeddedMavenExecutor EMBEDDED_MAVEN_EXECUTOR = new EmbeddedMavenExecutor(); + private static final EmbeddedMavenExecutor EMBEDDED_MAVEN_EXECUTOR = new EmbeddedMavenExecutor(MAVEN_HOME); /** * Keep executor alive, as long as Verifier is in classloader. For forked this means nothing, but is - * at least "handled the same" as embedded counterpart. Later on, we could have some similar solution like - * mvnd has, and keep pool of "hot" processes maybe? + * at least "handled the same" as embedded counterpart. Points to test subjected Maven home. */ - private static final ForkedMavenExecutor FORKED_MAVEN_EXECUTOR = new ForkedMavenExecutor(); + private static final ForkedMavenExecutor FORKED_MAVEN_EXECUTOR = new ForkedMavenExecutor(MAVEN_HOME); /** * The "preferred" fork mode of Verifier, defaults to "auto". In fact, am unsure is any other fork mode usable, @@ -164,13 +168,11 @@ public Verifier(String basedir, List defaultCliArguments, boolean create this.userHomeDirectory = Paths.get(System.getProperty("maven.test.user.home", "user.home")); Files.createDirectories(this.userHomeDirectory); this.outerLocalRepository = Paths.get(System.getProperty("maven.test.repo.outer", ".m2/repository")); - this.executorHelper = new HelperImpl( + this.executorHelper = ExecutorHelper.forExecutors( VERIFIER_FORK_MODE, - Paths.get(System.getProperty("maven.home")), - this.userHomeDirectory, EMBEDDED_MAVEN_EXECUTOR, FORKED_MAVEN_EXECUTOR); - this.executorTool = new ToolboxTool(executorHelper, toolboxVersion); + this.executorTool = new ToolboxExecutorTool(executorHelper, toolboxVersion); this.defaultCliArguments = new ArrayList<>(defaultCliArguments != null ? defaultCliArguments : DEFAULT_CLI_ARGUMENTS); this.logFile = this.basedir.resolve(logFileName); @@ -248,11 +250,9 @@ public void execute() throws VerificationException { args.add("-Daether.remoteRepositoryFilter.prefixes=false"); try { - ExecutorRequest.Builder builder = executorHelper - .executorRequest() + ExecutorRequest.Builder builder = executorRequest() .command(executable) .cwd(basedir) - .userHomeDirectory(userHomeDirectory) .jvmArguments(jvmArguments) .arguments(args) .skipMavenRc(skipMavenRc); @@ -442,10 +442,8 @@ public String getLocalRepositoryWithSettings(String settingsXml) { if (!Files.isRegularFile(settingsFile)) { throw new IllegalArgumentException("settings xml does not exist: " + settingsXml); } - return executorTool.localRepository(executorHelper - .executorRequest() + return executorTool.localRepository(executorRequest() .cwd(tempBasedir) - .userHomeDirectory(userHomeDirectory) .argument("-s") .argument(settingsFile.toString())); } else { @@ -454,7 +452,7 @@ public String getLocalRepositoryWithSettings(String settingsXml) { return outerHead; } else { return executorTool.localRepository( - executorHelper.executorRequest().cwd(tempBasedir).userHomeDirectory(userHomeDirectory)); + executorRequest().cwd(tempBasedir)); } } } @@ -476,12 +474,12 @@ private String formatCommandLine(ExecutorRequest request, ExecutorHelper.Mode mo cmdLine.append("# Command line: "); // Add the Maven executable path - Path mavenExecutable = request.installationDirectory() + Path mavenExecutable = MAVEN_HOME .resolve("bin") - .resolve(System.getProperty("os.name").toLowerCase().contains("windows") + .resolve(Executor.IS_WINDOWS ? request.command() + ".cmd" : request.command()); - cmdLine.append(mavenExecutable.toString()); + cmdLine.append(mavenExecutable); // Add MAVEN_ARGS if they would be used (only for forked mode) if (mode == ExecutorHelper.Mode.FORKED || mode == ExecutorHelper.Mode.AUTO) { @@ -865,7 +863,11 @@ public String getArtifactPath(String gid, String aid, String version, String ext } return getLocalRepository() + File.separator - + executorTool.artifactPath(executorHelper.executorRequest(), gav, null); + + executorTool.artifactPath(executorRequest(), gav, null); + } + + private ExecutorRequest.Builder executorRequest() { + return ExecutorRequest.mavenBuilder().userHomeDirectory(userHomeDirectory); } private String getSupportArtifactPath(String artifact) { @@ -923,7 +925,7 @@ public String getSupportArtifactPath(String gid, String aid, String version, Str } return outerLocalRepository .resolve(executorTool.artifactPath( - executorHelper.executorRequest().argument("-Dmaven.repo.local=" + outerLocalRepository), + executorRequest().argument("-Dmaven.repo.local=" + outerLocalRepository), gav, null)) .toString(); @@ -997,7 +999,7 @@ public String getArtifactMetadataPath(String gid, String aid, String version, St gav += filename; return getLocalRepository() + File.separator - + executorTool.metadataPath(executorHelper.executorRequest(), gav, repoId); + + executorTool.metadataPath(executorRequest(), gav, repoId); } /** @@ -1027,7 +1029,7 @@ public void deleteArtifact(String org, String name, String version, String ext) * @since 1.2 */ public void deleteArtifacts(String gid) throws IOException { - String mdPath = executorTool.metadataPath(executorHelper.executorRequest(), gid, null); + String mdPath = executorTool.metadataPath(executorRequest(), gid, null); Path dir = Paths.get(getLocalRepository()).resolve(mdPath).getParent(); FileUtils.deleteDirectory(dir.toFile()); } @@ -1047,7 +1049,7 @@ public void deleteArtifacts(String gid, String aid, String version) throws IOExc requireNonNull(version, "version is null"); String mdPath = - executorTool.metadataPath(executorHelper.executorRequest(), gid + ":" + aid + ":" + version, null); + executorTool.metadataPath(executorRequest(), gid + ":" + aid + ":" + version, null); Path dir = Paths.get(getLocalRepository()).resolve(mdPath).getParent(); FileUtils.deleteDirectory(dir.toFile()); } diff --git a/its/pom.xml b/its/pom.xml index 6aae412cbed8..96d795975c37 100644 --- a/its/pom.xml +++ b/its/pom.xml @@ -263,10 +263,11 @@ under the License. maven-compat ${maven-version} + - org.apache.maven + org.apache.maven.executor maven-executor - ${maven-version} + 1.0.0-SNAPSHOT