diff --git a/.mvn/maven.config b/.mvn/maven.config
new file mode 100644
index 000000000000..a4e2970377ca
--- /dev/null
+++ b/.mvn/maven.config
@@ -0,0 +1 @@
+-Daether.transport.jdk.httpVersion=HTTP_1_1
\ No newline at end of file
diff --git a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/Executor.java b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/Executor.java
new file mode 100644
index 000000000000..60cd38921a0f
--- /dev/null
+++ b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/Executor.java
@@ -0,0 +1,55 @@
+/*
+ * 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.api.cli;
+
+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 {
+ /**
+ * 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;
+
+ /**
+ * 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
+ default void close() throws ExecutorException {}
+}
diff --git a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/ExecutorException.java b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/ExecutorException.java
new file mode 100644
index 000000000000..8ce5bf157dbf
--- /dev/null
+++ b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/ExecutorException.java
@@ -0,0 +1,52 @@
+/*
+ * 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.api.cli;
+
+import org.apache.maven.api.annotations.Experimental;
+import org.apache.maven.api.annotations.Nullable;
+import org.apache.maven.api.services.MavenException;
+
+/**
+ * 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 MavenException {
+ /**
+ * 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/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/ExecutorRequest.java b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/ExecutorRequest.java
new file mode 100644
index 000000000000..811fe87686a4
--- /dev/null
+++ b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/ExecutorRequest.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.api.cli;
+
+import java.nio.file.Path;
+import java.util.List;
+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;
+
+/**
+ * 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 were not parsed, they are just passed over
+ * to executed tool.
+ *
+ * @since 4.0.0
+ */
+@Immutable
+@Experimental
+public interface ExecutorRequest {
+ /**
+ * The parser request this instance was created from.
+ */
+ @Nonnull
+ ParserRequest parserRequest();
+
+ /**
+ * 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 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 and invokers that spawn a new JVM.
+ *
+ * @return an Optional containing the list of extra JVM arguments, or empty if not specified
+ */
+ @Nonnull
+ Optional> jvmArguments();
+}
diff --git a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/Invoker.java b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/Invoker.java
index bc5f879895ea..70b54ba20461 100644
--- a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/Invoker.java
+++ b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/Invoker.java
@@ -24,18 +24,16 @@
/**
* Defines the contract for a component responsible for invoking a Maven application
* using the information provided in an {@link InvokerRequest}. This interface is central
- * to the execution of Maven commands and builds.
+ * to the construction and invocation of Maven commands and builds, and it fully parses arguments.
*
* The Invoker is designed to be flexible, allowing for different implementations
* that can handle various types of {@link InvokerRequest InvokerRequests}. It also implements
* {@link AutoCloseable} to ensure proper resource management.
*
- * @param The specific type of {@link InvokerRequest} this {@code Invoker} can handle, extending {@link InvokerRequest}
- *
* @since 4.0.0
*/
@Experimental
-public interface Invoker> extends AutoCloseable {
+public interface Invoker extends AutoCloseable {
/**
* Invokes the Maven application using the provided {@link InvokerRequest}.
* This method is responsible for executing the Maven command or build
@@ -45,7 +43,7 @@ public interface Invoker> extends Au
* @return an integer representing the exit code of the invocation (0 typically indicates success)
* @throws InvokerException if an error occurs during the invocation process
*/
- int invoke(@Nonnull R invokerRequest) throws InvokerException;
+ int invoke(@Nonnull InvokerRequest invokerRequest) throws InvokerException;
/**
* Closes and disposes of this {@link Invoker} instance, releasing any resources it may hold.
diff --git a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/InvokerException.java b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/InvokerException.java
index 08c8df20efd0..d1a479b71127 100644
--- a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/InvokerException.java
+++ b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/InvokerException.java
@@ -49,4 +49,21 @@ public InvokerException(@Nullable String message) {
public InvokerException(@Nullable String message, @Nullable Throwable cause) {
super(message, cause);
}
+
+ /**
+ * Exception for intentional exit: No message or anything will be displayed, just the
+ * carried exit code will be returned from invoker {@link Invoker#invoke(InvokerRequest)} method.
+ */
+ public static final class ExitException extends InvokerException {
+ private final int exitCode;
+
+ public ExitException(int exitCode) {
+ super("EXIT");
+ this.exitCode = exitCode;
+ }
+
+ public int getExitCode() {
+ return exitCode;
+ }
+ }
}
diff --git a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/InvokerRequest.java b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/InvokerRequest.java
index d38d0cc6334d..ed019663c6bf 100644
--- a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/InvokerRequest.java
+++ b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/InvokerRequest.java
@@ -33,22 +33,14 @@
import org.apache.maven.api.services.MessageBuilderFactory;
/**
- * Represents a Maven execution request, encapsulating all necessary information
- * for invoking a Maven build or command.
- *
- * @param the type of {@link Options} used for this request, extending the base {@link Options} interface
+ * Represents a Maven invocation request, encapsulating all necessary information
+ * for invoking a Maven build or command. Arguments are parsed and exposed via methods.
*
* @since 4.0.0
*/
@Immutable
@Experimental
-public interface InvokerRequest {
- /**
- * The parser request this instance was created from.
- */
- @Nonnull
- ParserRequest parserRequest();
-
+public interface InvokerRequest extends ExecutorRequest {
/**
* Shorthand for {@link Logger} to use.
*/
@@ -70,33 +62,6 @@ default Lookup lookup() {
return parserRequest().lookup();
}
- /**
- * 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 a map of user-defined properties for the Maven execution.
* These properties can be set using the -D command-line option.
@@ -172,5 +137,5 @@ default Lookup lookup() {
* @return the options object
*/
@Nonnull
- O options();
+ Options options();
}
diff --git a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/Parser.java b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/Parser.java
index a47a76f4e79c..6b791cbaaecd 100644
--- a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/Parser.java
+++ b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/Parser.java
@@ -22,47 +22,35 @@
import org.apache.maven.api.annotations.Experimental;
import org.apache.maven.api.annotations.Nonnull;
-import org.apache.maven.api.services.MessageBuilderFactory;
/**
- * Defines the contract for parsing Maven command-line arguments and creating an InvokerRequest.
- * This interface is responsible for interpreting the command-line input and constructing
- * the appropriate {@link InvokerRequest} object.
- *
- * @param the type of {@link InvokerRequest} produced by this parser, extending {@link InvokerRequest}
+ * Defines the contract for parsing Maven command-line arguments and creating an execution or invoker requests.
*
* @since 4.0.0
*/
@Experimental
-public interface Parser> {
+public interface Parser {
/**
- * Parses the given Maven arguments to create an InvokerRequest.
- * This is a convenience method that internally creates a ParserRequest using
- * {@link ParserRequest#mvn(String[], Logger, MessageBuilderFactory)}.
+ * Parses the given ParserRequest to create an {@link ExecutorRequest}.
+ * This method does not interpret tool arguments.
*
- * @param args the command-line arguments
- * @param logger the logger to use during parsing
- * @param messageBuilderFactory the factory for creating message builders
- * @return the parsed InvokerRequest
- * @throws ParserException if there's an error during parsing of the command or arguments
+ * @param parserRequest the request containing all necessary information for parsing
+ * @return the parsed executor request
+ * @throws ParserException if there's an error during parsing of the request
* @throws IOException if there's an I/O error during the parsing process
*/
@Nonnull
- default R mvn(@Nonnull String[] args, @Nonnull Logger logger, @Nonnull MessageBuilderFactory messageBuilderFactory)
- throws ParserException, IOException {
- return parse(ParserRequest.mvn(args, logger, messageBuilderFactory).build());
- }
+ ExecutorRequest parseExecution(@Nonnull ParserRequest parserRequest) throws ParserException, IOException;
/**
- * Parses the given ParserRequest to create an InvokerRequest.
- * This method is responsible for interpreting the contents of the ParserRequest
- * and constructing the appropriate InvokerRequest object.
+ * Parses the given ParserRequest to create an {@link InvokerRequest}.
+ * This method does interpret tool arguments.
*
* @param parserRequest the request containing all necessary information for parsing
- * @return the parsed InvokerRequest
+ * @return the parsed invoker request
* @throws ParserException if there's an error during parsing of the request
* @throws IOException if there's an I/O error during the parsing process
*/
@Nonnull
- R parse(@Nonnull ParserRequest parserRequest) throws ParserException, IOException;
+ InvokerRequest parseInvocation(@Nonnull ParserRequest parserRequest) throws ParserException, IOException;
}
diff --git a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/ParserRequest.java b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/ParserRequest.java
index ae5f83ec4b6b..014576b4925d 100644
--- a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/ParserRequest.java
+++ b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/ParserRequest.java
@@ -46,12 +46,6 @@
@Immutable
@Experimental
public interface ParserRequest {
- String MVN_CMD = "mvn";
- String MVN_NAME = "Maven";
-
- String MVNENC_CMD = "mvnenc";
- String MVNENC_NAME = "Maven Password Encrypting Tool";
-
/**
* Returns the Maven command to be executed. This command is used in some invokers (ie forked) but also to
* present help to user.
@@ -179,7 +173,7 @@ static Builder mvn(
@Nonnull
static Builder mvn(
@Nonnull List args, @Nonnull Logger logger, @Nonnull MessageBuilderFactory messageBuilderFactory) {
- return builder(MVN_CMD, MVN_NAME, args, logger, messageBuilderFactory);
+ return builder(Tools.MVN_CMD, Tools.MVN_NAME, args, logger, messageBuilderFactory);
}
/**
@@ -207,7 +201,7 @@ static Builder mvnenc(
@Nonnull
static Builder mvnenc(
@Nonnull List args, @Nonnull Logger logger, @Nonnull MessageBuilderFactory messageBuilderFactory) {
- return builder(MVNENC_CMD, MVNENC_NAME, args, logger, messageBuilderFactory);
+ return builder(Tools.MVNENC_CMD, Tools.MVNENC_NAME, args, logger, messageBuilderFactory);
}
/**
diff --git a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvn/local/LocalMavenInvoker.java b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/Tools.java
similarity index 67%
rename from api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvn/local/LocalMavenInvoker.java
rename to api/maven-api-cli/src/main/java/org/apache/maven/api/cli/Tools.java
index ed429c2220e2..b237dc54145b 100644
--- a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvn/local/LocalMavenInvoker.java
+++ b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/Tools.java
@@ -16,17 +16,24 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.maven.api.cli.mvn.local;
+package org.apache.maven.api.cli;
import org.apache.maven.api.annotations.Experimental;
-import org.apache.maven.api.cli.mvn.MavenInvoker;
-import org.apache.maven.api.cli.mvn.MavenInvokerRequest;
-import org.apache.maven.api.cli.mvn.MavenOptions;
+import org.apache.maven.api.annotations.Immutable;
/**
- * Local Maven invoker, it expects all the Maven be present in classpath.
+ * Represents most common tools supported by CLIng.
*
* @since 4.0.0
*/
+@Immutable
@Experimental
-public interface LocalMavenInvoker extends MavenInvoker> {}
+public final class Tools {
+ private Tools() {}
+
+ public static final String MVN_CMD = "mvn";
+ public static final String MVN_NAME = "Maven";
+
+ public static final String MVNENC_CMD = "mvnenc";
+ public static final String MVNENC_NAME = "Maven Password Encrypting Tool";
+}
diff --git a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvn/MavenInvoker.java b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvn/MavenInvoker.java
deleted file mode 100644
index c7e96730645e..000000000000
--- a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvn/MavenInvoker.java
+++ /dev/null
@@ -1,47 +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.api.cli.mvn;
-
-import org.apache.maven.api.annotations.Experimental;
-import org.apache.maven.api.annotations.Nonnull;
-import org.apache.maven.api.cli.Invoker;
-import org.apache.maven.api.cli.InvokerException;
-
-/**
- * Defines the contract for a component responsible for invoking Maven using information provided in an invoker request.
- * This interface extends the general {@link Invoker} interface, specializing it for Maven-specific operations.
- *
- * @param The specific type of {@link MavenInvokerRequest} this invoker can handle
- *
- * @since 4.0.0
- */
-@Experimental
-public interface MavenInvoker> extends Invoker {
- /**
- * Invokes Maven using the provided MavenInvokerRequest.
- * This method is responsible for executing the Maven build process
- * based on the information contained in the request.
- *
- * @param invokerRequest the request containing all necessary information for the Maven invocation
- * @return an integer representing the exit code of the Maven invocation (0 typically indicates success)
- * @throws InvokerException if an error occurs during the Maven invocation process
- */
- @Override
- int invoke(@Nonnull R invokerRequest) throws InvokerException;
-}
diff --git a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvn/MavenInvokerRequest.java b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvn/MavenInvokerRequest.java
deleted file mode 100644
index 4030c5320b16..000000000000
--- a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvn/MavenInvokerRequest.java
+++ /dev/null
@@ -1,39 +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.api.cli.mvn;
-
-import org.apache.maven.api.annotations.Experimental;
-import org.apache.maven.api.cli.InvokerRequest;
-
-/**
- * Represents a request to invoke Maven.
- * This interface extends the general {@link InvokerRequest}, specializing it for Maven-specific operations.
- *
- * A {@link MavenInvokerRequest} encapsulates all the necessary information needed to perform
- * a Maven build, including any Maven-specific options defined in {@link MavenOptions}.
- *
- * @param The specific Options type this request carries
- *
- * @since 4.0.0
- */
-@Experimental
-public interface MavenInvokerRequest extends InvokerRequest {
- // This interface doesn't declare any additional methods beyond those inherited from InvokerRequest.
- // It serves to type-specify the Options as MavenOptions for Maven-specific requests.
-}
diff --git a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvn/MavenParser.java b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvn/MavenParser.java
deleted file mode 100644
index bb9e5bcb8cb5..000000000000
--- a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvn/MavenParser.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.api.cli.mvn;
-
-import java.io.IOException;
-
-import org.apache.maven.api.annotations.Experimental;
-import org.apache.maven.api.annotations.Nonnull;
-import org.apache.maven.api.cli.Parser;
-import org.apache.maven.api.cli.ParserException;
-import org.apache.maven.api.cli.ParserRequest;
-
-/**
- * Defines the contract for parsing Maven-specific command-line arguments and creating a MavenInvokerRequest.
- * This interface extends the general {@link Parser} interface, specializing it for Maven operations.
- *
- * @param The specific type of MavenInvokerRequest this parser produces
- *
- * @since 4.0.0
- */
-@Experimental
-public interface MavenParser> extends Parser {
- /**
- * Parses the given {@link ParserRequest} to create a {@link MavenInvokerRequest}.
- * This method is responsible for interpreting the contents of the ParserRequest
- * and constructing the appropriate {@link MavenInvokerRequest} object for Maven operations.
- *
- * @param parserRequest the request containing all necessary information for parsing
- * @return the parsed {@link MavenInvokerRequest}
- * @throws ParserException if there's an error during parsing of the request
- * @throws IOException if there's an I/O error during the parsing process
- */
- @Nonnull
- R parse(@Nonnull ParserRequest parserRequest) throws ParserException, IOException;
-}
diff --git a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvn/forked/ForkedMavenInvokerRequest.java b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvn/forked/ForkedMavenInvokerRequest.java
deleted file mode 100644
index f8a363763fe9..000000000000
--- a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvn/forked/ForkedMavenInvokerRequest.java
+++ /dev/null
@@ -1,45 +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.api.cli.mvn.forked;
-
-import java.util.List;
-import java.util.Optional;
-
-import org.apache.maven.api.annotations.Experimental;
-import org.apache.maven.api.annotations.Nonnull;
-import org.apache.maven.api.cli.mvn.MavenInvokerRequest;
-import org.apache.maven.api.cli.mvn.MavenOptions;
-
-/**
- * Represents a request to invoke Maven in a forked JVM.
- * This interface extends the {@link MavenInvokerRequest}, adding capabilities specific to forked Maven executions.
- *
- * @since 4.0.0
- */
-@Experimental
-public interface ForkedMavenInvokerRequest extends MavenInvokerRequest {
- /**
- * Returns the list of extra JVM arguments to be passed to the forked Maven process.
- * These arguments allow for customization of the JVM environment in which Maven will run.
- *
- * @return an Optional containing the list of extra JVM arguments, or empty if not specified
- */
- @Nonnull
- Optional> jvmArguments();
-}
diff --git a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvn/forked/ForkedMavenParser.java b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvn/forked/ForkedMavenParser.java
deleted file mode 100644
index 8d21f864c0d9..000000000000
--- a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvn/forked/ForkedMavenParser.java
+++ /dev/null
@@ -1,49 +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.api.cli.mvn.forked;
-
-import java.io.IOException;
-
-import org.apache.maven.api.annotations.Experimental;
-import org.apache.maven.api.annotations.Nonnull;
-import org.apache.maven.api.cli.ParserException;
-import org.apache.maven.api.cli.ParserRequest;
-import org.apache.maven.api.cli.mvn.MavenParser;
-
-/**
- * Defines the contract for parsing command-line arguments specific to forked Maven executions.
- * This interface extends the {@link MavenParser}, specializing it for creating {@link ForkedMavenInvokerRequest} objects.
- *
- * @since 4.0.0
- */
-@Experimental
-public interface ForkedMavenParser extends MavenParser {
- /**
- * Parses the given ParserRequest to create a ForkedMavenInvokerRequest.
- * This method is responsible for interpreting the contents of the ParserRequest
- * and constructing the appropriate ForkedMavenInvokerRequest object for forked Maven operations.
- *
- * @param parserRequest the request containing all necessary information for parsing
- * @return the parsed ForkedMavenInvokerRequest
- * @throws ParserException if there's an error during parsing of the request
- * @throws IOException if there's an I/O error during the parsing process
- */
- @Nonnull
- ForkedMavenInvokerRequest parse(@Nonnull ParserRequest parserRequest) throws ParserException, IOException;
-}
diff --git a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvnenc/EncryptInvoker.java b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvnenc/EncryptInvoker.java
deleted file mode 100644
index 875b4c1c0107..000000000000
--- a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvnenc/EncryptInvoker.java
+++ /dev/null
@@ -1,47 +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.api.cli.mvnenc;
-
-import org.apache.maven.api.annotations.Experimental;
-import org.apache.maven.api.cli.Invoker;
-import org.apache.maven.api.cli.InvokerException;
-
-/**
- * Defines the contract for a component responsible for invoking the Maven encryption tool.
- * This interface extends the general Invoker interface, specializing it for encryption-related operations.
- *
- * The EncryptInvoker is designed to handle encryption tasks within the Maven ecosystem,
- * such as encrypting passwords or other sensitive information in Maven settings files.
- *
- * @since 4.0.0
- */
-@Experimental
-public interface EncryptInvoker extends Invoker {
- /**
- * Invokes the encryption tool using the provided EncryptInvokerRequest.
- * This method is responsible for executing the encryption command or process
- * based on the information contained in the request.
- *
- * @param invokerRequest the request containing all necessary information for the encryption invocation
- * @return an integer representing the exit code of the invocation (0 typically indicates success)
- * @throws InvokerException if an error occurs during the encryption process
- */
- @Override
- int invoke(EncryptInvokerRequest invokerRequest) throws InvokerException;
-}
diff --git a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvnenc/EncryptInvokerRequest.java b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvnenc/EncryptInvokerRequest.java
deleted file mode 100644
index 229ee7d22c65..000000000000
--- a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvnenc/EncryptInvokerRequest.java
+++ /dev/null
@@ -1,37 +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.api.cli.mvnenc;
-
-import org.apache.maven.api.annotations.Experimental;
-import org.apache.maven.api.cli.InvokerRequest;
-
-/**
- * Represents a request to invoke the Maven encryption tool.
- * This interface extends the general InvokerRequest, specializing it for encryption-related operations.
- *
- * An EncryptInvokerRequest encapsulates all the necessary information needed to perform
- * an encryption operation, including any encryption-specific options defined in EncryptOptions.
- *
- * @since 4.0.0
- */
-@Experimental
-public interface EncryptInvokerRequest extends InvokerRequest {
- // This interface doesn't declare any additional methods beyond those inherited from InvokerRequest.
- // It serves to type-specify the Options as EncryptOptions for encryption-related requests.
-}
diff --git a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvnenc/EncryptParser.java b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvnenc/EncryptParser.java
deleted file mode 100644
index bf3eb9d7e690..000000000000
--- a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvnenc/EncryptParser.java
+++ /dev/null
@@ -1,49 +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.api.cli.mvnenc;
-
-import java.io.IOException;
-
-import org.apache.maven.api.annotations.Experimental;
-import org.apache.maven.api.annotations.Nonnull;
-import org.apache.maven.api.cli.Parser;
-import org.apache.maven.api.cli.ParserException;
-import org.apache.maven.api.cli.ParserRequest;
-
-/**
- * Defines the contract for parsing encryption-related command-line arguments and creating an EncryptInvokerRequest.
- * This interface extends the general {@link Parser} interface, specializing it for encryption operations.
- *
- * @since 4.0.0
- */
-@Experimental
-public interface EncryptParser extends Parser {
- /**
- * Parses the given ParserRequest to create an EncryptInvokerRequest.
- * This method is responsible for interpreting the contents of the ParserRequest
- * and constructing the appropriate EncryptInvokerRequest object for encryption operations.
- *
- * @param parserRequest the request containing all necessary information for parsing
- * @return the parsed EncryptInvokerRequest
- * @throws ParserException if there's an error during parsing of the request
- * @throws IOException if there's an I/O error during the parsing process
- */
- @Nonnull
- EncryptInvokerRequest parse(@Nonnull ParserRequest parserRequest) throws ParserException, IOException;
-}
diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/ClingSupport.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/ClingSupport.java
index ba65331388fe..3f1a207520fe 100644
--- a/impl/maven-cli/src/main/java/org/apache/maven/cling/ClingSupport.java
+++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/ClingSupport.java
@@ -23,7 +23,6 @@
import org.apache.maven.api.cli.Invoker;
import org.apache.maven.api.cli.InvokerException;
import org.apache.maven.api.cli.InvokerRequest;
-import org.apache.maven.api.cli.Options;
import org.apache.maven.api.cli.ParserException;
import org.codehaus.plexus.classworlds.ClassWorld;
@@ -31,11 +30,8 @@
/**
* The CLI "new-gen".
- *
- * @param the options type
- * @param the request type
*/
-public abstract class ClingSupport> {
+public abstract class ClingSupport {
static final String CORE_CLASS_REALM_ID = "plexus.core";
protected final ClassWorld classWorld;
@@ -64,7 +60,7 @@ private ClingSupport(ClassWorld classWorld, boolean classWorldManaged) {
* The main entry point.
*/
public int run(String[] args) throws IOException {
- try (Invoker invoker = createInvoker()) {
+ try (Invoker invoker = createInvoker()) {
return invoker.invoke(parseArguments(args));
} catch (ParserException e) {
System.err.println(e.getMessage());
@@ -78,7 +74,7 @@ public int run(String[] args) throws IOException {
}
}
- protected abstract Invoker createInvoker();
+ protected abstract Invoker createInvoker();
- protected abstract R parseArguments(String[] args) throws ParserException, IOException;
+ protected abstract InvokerRequest parseArguments(String[] args) throws ParserException, IOException;
}
diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenCling.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenCling.java
index b8b204d5f470..d4869f2186f3 100644
--- a/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenCling.java
+++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenCling.java
@@ -21,20 +21,20 @@
import java.io.IOException;
import org.apache.maven.api.cli.Invoker;
+import org.apache.maven.api.cli.InvokerRequest;
import org.apache.maven.api.cli.ParserException;
-import org.apache.maven.api.cli.mvn.MavenInvokerRequest;
-import org.apache.maven.api.cli.mvn.MavenOptions;
+import org.apache.maven.api.cli.ParserRequest;
import org.apache.maven.cling.invoker.ProtoLogger;
import org.apache.maven.cling.invoker.ProtoLookup;
-import org.apache.maven.cling.invoker.mvn.DefaultMavenParser;
-import org.apache.maven.cling.invoker.mvn.local.DefaultLocalMavenInvoker;
+import org.apache.maven.cling.invoker.mvn.MavenParser;
+import org.apache.maven.cling.invoker.mvn.local.LocalMavenInvoker;
import org.apache.maven.jline.JLineMessageBuilderFactory;
import org.codehaus.plexus.classworlds.ClassWorld;
/**
* Maven CLI "new-gen".
*/
-public class MavenCling extends ClingSupport> {
+public class MavenCling extends ClingSupport {
/**
* "Normal" Java entry point. Note: Maven uses ClassWorld Launcher and this entry point is NOT used under normal
* circumstances.
@@ -60,13 +60,15 @@ public MavenCling(ClassWorld classWorld) {
}
@Override
- protected Invoker> createInvoker() {
- return new DefaultLocalMavenInvoker(
+ protected Invoker createInvoker() {
+ return new LocalMavenInvoker(
ProtoLookup.builder().addMapping(ClassWorld.class, classWorld).build());
}
@Override
- protected MavenInvokerRequest parseArguments(String[] args) throws ParserException, IOException {
- return new DefaultMavenParser().mvn(args, new ProtoLogger(), new JLineMessageBuilderFactory());
+ protected InvokerRequest parseArguments(String[] args) throws ParserException, IOException {
+ return new MavenParser()
+ .parseInvocation(ParserRequest.mvn(args, new ProtoLogger(), new JLineMessageBuilderFactory())
+ .build());
}
}
diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenEncCling.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenEncCling.java
index 488eb9eae44a..b1f2f861badb 100644
--- a/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenEncCling.java
+++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenEncCling.java
@@ -21,21 +21,20 @@
import java.io.IOException;
import org.apache.maven.api.cli.Invoker;
+import org.apache.maven.api.cli.InvokerRequest;
import org.apache.maven.api.cli.ParserException;
import org.apache.maven.api.cli.ParserRequest;
-import org.apache.maven.api.cli.mvnenc.EncryptInvokerRequest;
-import org.apache.maven.api.cli.mvnenc.EncryptOptions;
import org.apache.maven.cling.invoker.ProtoLogger;
import org.apache.maven.cling.invoker.ProtoLookup;
-import org.apache.maven.cling.invoker.mvnenc.DefaultEncryptInvoker;
-import org.apache.maven.cling.invoker.mvnenc.DefaultEncryptParser;
+import org.apache.maven.cling.invoker.mvnenc.EncryptInvoker;
+import org.apache.maven.cling.invoker.mvnenc.EncryptParser;
import org.apache.maven.jline.JLineMessageBuilderFactory;
import org.codehaus.plexus.classworlds.ClassWorld;
/**
* Maven encrypt CLI "new-gen".
*/
-public class MavenEncCling extends ClingSupport {
+public class MavenEncCling extends ClingSupport {
/**
* "Normal" Java entry point. Note: Maven uses ClassWorld Launcher and this entry point is NOT used under normal
* circumstances.
@@ -61,15 +60,15 @@ public MavenEncCling(ClassWorld classWorld) {
}
@Override
- protected Invoker createInvoker() {
- return new DefaultEncryptInvoker(
+ protected Invoker createInvoker() {
+ return new EncryptInvoker(
ProtoLookup.builder().addMapping(ClassWorld.class, classWorld).build());
}
@Override
- protected EncryptInvokerRequest parseArguments(String[] args) throws ParserException, IOException {
- return new DefaultEncryptParser()
- .parse(ParserRequest.mvnenc(args, new ProtoLogger(), new JLineMessageBuilderFactory())
+ protected InvokerRequest parseArguments(String[] args) throws ParserException, IOException {
+ return new EncryptParser()
+ .parseInvocation(ParserRequest.mvnenc(args, new ProtoLogger(), new JLineMessageBuilderFactory())
.build());
}
}
diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/BaseExecutorRequest.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/BaseExecutorRequest.java
new file mode 100644
index 000000000000..00dbef917702
--- /dev/null
+++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/BaseExecutorRequest.java
@@ -0,0 +1,77 @@
+/*
+ * 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.invoker;
+
+import java.nio.file.Path;
+import java.util.List;
+import java.util.Optional;
+
+import org.apache.maven.api.annotations.Nonnull;
+import org.apache.maven.api.annotations.Nullable;
+import org.apache.maven.api.cli.ExecutorRequest;
+import org.apache.maven.api.cli.ParserRequest;
+
+import static java.util.Objects.requireNonNull;
+
+public class BaseExecutorRequest implements ExecutorRequest {
+ private final ParserRequest parserRequest;
+ private final Path cwd;
+ private final Path installationDirectory;
+ private final Path userHomeDirectory;
+ private final List jvmArguments;
+
+ @SuppressWarnings("ParameterNumber")
+ public BaseExecutorRequest(
+ @Nonnull ParserRequest parserRequest,
+ @Nonnull Path cwd,
+ @Nonnull Path installationDirectory,
+ @Nonnull Path userHomeDirectory,
+ @Nullable List jvmArguments) {
+ this.parserRequest = requireNonNull(parserRequest);
+ this.cwd = requireNonNull(cwd);
+ this.installationDirectory = requireNonNull(installationDirectory);
+ this.userHomeDirectory = requireNonNull(userHomeDirectory);
+ this.jvmArguments = jvmArguments;
+ }
+
+ @Override
+ public ParserRequest parserRequest() {
+ return parserRequest;
+ }
+
+ @Override
+ public Path cwd() {
+ return cwd;
+ }
+
+ @Override
+ public Path installationDirectory() {
+ return installationDirectory;
+ }
+
+ @Override
+ public Path userHomeDirectory() {
+ return userHomeDirectory;
+ }
+
+ @Override
+ public Optional> jvmArguments() {
+ return Optional.ofNullable(jvmArguments);
+ }
+}
diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/BaseInvokerRequest.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/BaseInvokerRequest.java
index 0b1bd847d704..25dcf754a766 100644
--- a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/BaseInvokerRequest.java
+++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/BaseInvokerRequest.java
@@ -28,26 +28,20 @@
import org.apache.maven.api.annotations.Nonnull;
import org.apache.maven.api.annotations.Nullable;
import org.apache.maven.api.cli.InvokerRequest;
-import org.apache.maven.api.cli.Options;
import org.apache.maven.api.cli.ParserRequest;
import org.apache.maven.api.cli.extensions.CoreExtension;
import static java.util.Objects.requireNonNull;
-public abstract class BaseInvokerRequest implements InvokerRequest {
- private final ParserRequest parserRequest;
- private final Path cwd;
- private final Path installationDirectory;
- private final Path userHomeDirectory;
+public abstract class BaseInvokerRequest extends BaseExecutorRequest implements InvokerRequest {
private final Map userProperties;
private final Map systemProperties;
private final Path topDirectory;
private final Path rootDirectory;
-
+ private final List coreExtensions;
private final InputStream in;
private final OutputStream out;
private final OutputStream err;
- private final List coreExtensions;
@SuppressWarnings("ParameterNumber")
public BaseInvokerRequest(
@@ -62,40 +56,18 @@ public BaseInvokerRequest(
@Nullable InputStream in,
@Nullable OutputStream out,
@Nullable OutputStream err,
- @Nullable List coreExtensions) {
- this.parserRequest = requireNonNull(parserRequest);
- this.cwd = requireNonNull(cwd);
- this.installationDirectory = requireNonNull(installationDirectory);
- this.userHomeDirectory = requireNonNull(userHomeDirectory);
+ @Nullable List coreExtensions,
+ @Nullable List jvmArguments) {
+ super(parserRequest, cwd, installationDirectory, userHomeDirectory, jvmArguments);
this.userProperties = requireNonNull(userProperties);
this.systemProperties = requireNonNull(systemProperties);
this.topDirectory = requireNonNull(topDirectory);
this.rootDirectory = rootDirectory;
+ this.coreExtensions = coreExtensions;
this.in = in;
this.out = out;
this.err = err;
- this.coreExtensions = coreExtensions;
- }
-
- @Override
- public ParserRequest parserRequest() {
- return parserRequest;
- }
-
- @Override
- public Path cwd() {
- return cwd;
- }
-
- @Override
- public Path installationDirectory() {
- return installationDirectory;
- }
-
- @Override
- public Path userHomeDirectory() {
- return userHomeDirectory;
}
@Override
diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/BaseParser.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/BaseParser.java
index 4dc2550d11ff..853fea00d8be 100644
--- a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/BaseParser.java
+++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/BaseParser.java
@@ -33,9 +33,11 @@
import java.util.Map;
import java.util.Properties;
import java.util.function.Function;
+import java.util.stream.Collectors;
import org.apache.maven.api.Constants;
import org.apache.maven.api.annotations.Nullable;
+import org.apache.maven.api.cli.ExecutorRequest;
import org.apache.maven.api.cli.InvokerRequest;
import org.apache.maven.api.cli.Options;
import org.apache.maven.api.cli.Parser;
@@ -57,7 +59,7 @@
import static org.apache.maven.cling.invoker.Utils.stripLeadingAndTrailingQuotes;
import static org.apache.maven.cling.invoker.Utils.toMap;
-public abstract class BaseParser> implements Parser {
+public abstract class BaseParser implements Parser {
@SuppressWarnings("VisibilityModifier")
public static class LocalContext {
@@ -93,7 +95,26 @@ public Map extraInterpolationSource() {
}
@Override
- public R parse(ParserRequest parserRequest) throws ParserException, IOException {
+ public ExecutorRequest parseExecution(ParserRequest parserRequest) throws ParserException, IOException {
+ requireNonNull(parserRequest);
+
+ LocalContext context = new LocalContext(parserRequest);
+
+ // the basics
+ context.cwd = requireNonNull(getCwd(context));
+ context.installationDirectory = requireNonNull(getInstallationDirectory(context));
+ context.userHomeDirectory = requireNonNull(getUserHomeDirectory(context));
+
+ return getExecutionRequest(context);
+ }
+
+ protected ExecutorRequest getExecutionRequest(LocalContext context) {
+ return new BaseExecutorRequest(
+ context.parserRequest, context.cwd, context.installationDirectory, context.userHomeDirectory, null);
+ }
+
+ @Override
+ public InvokerRequest parseInvocation(ParserRequest parserRequest) throws ParserException, IOException {
requireNonNull(parserRequest);
LocalContext context = new LocalContext(parserRequest);
@@ -108,7 +129,7 @@ public R parse(ParserRequest parserRequest) throws ParserException, IOException
context.rootDirectory = getRootDirectory(context);
// options
- List parsedOptions = parseCliOptions(context);
+ List parsedOptions = parseCliOptions(context);
// warn about deprecated options
PrintWriter printWriter = new PrintWriter(parserRequest.out() != null ? parserRequest.out() : System.out, true);
@@ -131,7 +152,7 @@ public R parse(ParserRequest parserRequest) throws ParserException, IOException
return getInvokerRequest(context);
}
- protected abstract R getInvokerRequest(LocalContext context);
+ protected abstract InvokerRequest getInvokerRequest(LocalContext context);
protected Path getCwd(LocalContext context) throws ParserException {
if (context.parserRequest.cwd() != null) {
@@ -271,9 +292,9 @@ protected Map populateUserProperties(LocalContext context) throw
return toMap(userProperties);
}
- protected abstract List parseCliOptions(LocalContext context) throws ParserException, IOException;
+ protected abstract List parseCliOptions(LocalContext context) throws ParserException, IOException;
- protected abstract O assembleOptions(List parsedOptions);
+ protected abstract Options assembleOptions(List parsedOptions);
protected List readCoreExtensionsDescriptor(LocalContext context)
throws ParserException, IOException {
@@ -304,4 +325,21 @@ protected List readCoreExtensionsDescriptorFromFile(Path extensio
throw new ParserException("Failed to parse extensions file: " + extensionsFile, e);
}
}
+
+ protected List getJvmArguments(Path rootDirectory) throws ParserException {
+ if (rootDirectory != null) {
+ Path jvmConfig = rootDirectory.resolve(".mvn/jvm.config");
+ if (Files.exists(jvmConfig)) {
+ try {
+ return Files.readAllLines(jvmConfig).stream()
+ .filter(l -> !l.isBlank() && !l.startsWith("#"))
+ .flatMap(l -> Arrays.stream(l.split(" ")))
+ .collect(Collectors.toList());
+ } catch (IOException e) {
+ throw new ParserException("Failed to read JVM configuration file: " + jvmConfig, e);
+ }
+ }
+ }
+ return null;
+ }
}
diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/ContainerCapsule.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/ContainerCapsule.java
index 39836fe043f8..a8c80ed983a9 100644
--- a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/ContainerCapsule.java
+++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/ContainerCapsule.java
@@ -18,6 +18,8 @@
*/
package org.apache.maven.cling.invoker;
+import java.util.Optional;
+
import org.apache.maven.api.annotations.Nonnull;
import org.apache.maven.api.cli.InvokerException;
import org.apache.maven.api.services.Lookup;
@@ -32,6 +34,12 @@ public interface ContainerCapsule extends AutoCloseable {
@Nonnull
Lookup getLookup();
+ /**
+ * The TCCL, if implementation requires it.
+ */
+ @Nonnull
+ Optional currentThreadClassLoader();
+
/**
* Performs a clean shutdown of backing container.
*/
diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/ContainerCapsuleFactory.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/ContainerCapsuleFactory.java
index ee01e3036f26..171980fea7dc 100644
--- a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/ContainerCapsuleFactory.java
+++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/ContainerCapsuleFactory.java
@@ -20,21 +20,16 @@
import org.apache.maven.api.annotations.Nonnull;
import org.apache.maven.api.cli.InvokerException;
-import org.apache.maven.api.cli.InvokerRequest;
-import org.apache.maven.api.cli.Options;
/**
* Container capsule factory.
*
- * @param the options type
- * @param the invoker request type
- * @param the invoker context type
+ * @param The context type.
*/
-public interface ContainerCapsuleFactory<
- O extends Options, R extends InvokerRequest, C extends LookupInvoker.LookupInvokerContext> {
+public interface ContainerCapsuleFactory {
/**
* Creates container capsule.
*/
@Nonnull
- ContainerCapsule createContainerCapsule(C context) throws InvokerException;
+ ContainerCapsule createContainerCapsule(LookupInvoker invoker, C context) throws InvokerException;
}
diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/LookupContext.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/LookupContext.java
new file mode 100644
index 000000000000..e5af6e8513d6
--- /dev/null
+++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/LookupContext.java
@@ -0,0 +1,109 @@
+/*
+ * 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.invoker;
+
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+import org.apache.maven.api.Session;
+import org.apache.maven.api.cli.InvokerException;
+import org.apache.maven.api.cli.InvokerRequest;
+import org.apache.maven.api.cli.Logger;
+import org.apache.maven.api.services.Lookup;
+import org.apache.maven.api.settings.Settings;
+import org.apache.maven.cling.invoker.mvn.ProtoSession;
+import org.apache.maven.cling.logging.Slf4jConfiguration;
+import org.jline.terminal.Terminal;
+import org.slf4j.ILoggerFactory;
+
+import static java.util.Objects.requireNonNull;
+
+@SuppressWarnings("VisibilityModifier")
+public class LookupContext implements AutoCloseable {
+ public final InvokerRequest invokerRequest;
+ public final Function cwdResolver;
+ public final Function installationResolver;
+ public final Function userResolver;
+ public final Session session;
+
+ protected LookupContext(InvokerRequest invokerRequest) {
+ this.invokerRequest = requireNonNull(invokerRequest);
+ this.cwdResolver = s -> invokerRequest.cwd().resolve(s).normalize().toAbsolutePath();
+ this.installationResolver = s ->
+ invokerRequest.installationDirectory().resolve(s).normalize().toAbsolutePath();
+ this.userResolver =
+ s -> invokerRequest.userHomeDirectory().resolve(s).normalize().toAbsolutePath();
+ this.logger = invokerRequest.parserRequest().logger();
+
+ Map user = new HashMap<>(invokerRequest.userProperties());
+ user.put("session.rootDirectory", invokerRequest.rootDirectory().toString());
+ user.put("session.topDirectory", invokerRequest.topDirectory().toString());
+ Map system = new HashMap<>(invokerRequest.systemProperties());
+ this.session = ProtoSession.create(user, system);
+ }
+
+ public Logger logger;
+ public ILoggerFactory loggerFactory;
+ public Slf4jConfiguration slf4jConfiguration;
+ public Slf4jConfiguration.Level loggerLevel;
+ public Boolean coloredOutput;
+ public Terminal terminal;
+ public Consumer writer;
+ public ContainerCapsule containerCapsule;
+ public Lookup lookup;
+
+ public boolean interactive;
+ public Path localRepositoryPath;
+ public Path installationSettingsPath;
+ public Path projectSettingsPath;
+ public Path userSettingsPath;
+ public Settings effectiveSettings;
+
+ public final List closeables = new ArrayList<>();
+
+ @Override
+ public void close() throws InvokerException {
+ List causes = null;
+ List cs = new ArrayList<>(closeables);
+ Collections.reverse(cs);
+ for (AutoCloseable c : cs) {
+ if (c != null) {
+ try {
+ c.close();
+ } catch (Exception e) {
+ if (causes == null) {
+ causes = new ArrayList<>();
+ }
+ causes.add(e);
+ }
+ }
+ }
+ if (causes != null) {
+ InvokerException exception = new InvokerException("Unable to close context");
+ causes.forEach(exception::addSuppressed);
+ throw exception;
+ }
+ }
+}
diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/LookupInvoker.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/LookupInvoker.java
index dc6ca2a698e1..ae229ec7c00f 100644
--- a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/LookupInvoker.java
+++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/LookupInvoker.java
@@ -24,8 +24,6 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
@@ -35,7 +33,6 @@
import java.util.function.Function;
import org.apache.maven.api.Constants;
-import org.apache.maven.api.Session;
import org.apache.maven.api.cli.Invoker;
import org.apache.maven.api.cli.InvokerException;
import org.apache.maven.api.cli.InvokerRequest;
@@ -43,7 +40,6 @@
import org.apache.maven.api.cli.Options;
import org.apache.maven.api.services.BuilderProblem;
import org.apache.maven.api.services.Interpolator;
-import org.apache.maven.api.services.Lookup;
import org.apache.maven.api.services.MavenException;
import org.apache.maven.api.services.MessageBuilder;
import org.apache.maven.api.services.SettingsBuilder;
@@ -62,7 +58,6 @@
import org.apache.maven.artifact.repository.MavenArtifactRepository;
import org.apache.maven.artifact.repository.layout.DefaultRepositoryLayout;
import org.apache.maven.bridge.MavenRepositorySystem;
-import org.apache.maven.cling.invoker.mvn.ProtoSession;
import org.apache.maven.cling.logging.Slf4jConfiguration;
import org.apache.maven.cling.logging.Slf4jConfigurationFactory;
import org.apache.maven.cling.transfer.ConsoleMavenTransferListener;
@@ -82,7 +77,6 @@
import org.jline.terminal.TerminalBuilder;
import org.jline.terminal.impl.AbstractPosixTerminal;
import org.jline.terminal.spi.TerminalExt;
-import org.slf4j.ILoggerFactory;
import org.slf4j.LoggerFactory;
import org.slf4j.spi.LocationAwareLogger;
@@ -91,108 +85,11 @@
import static org.apache.maven.cling.invoker.Utils.toProperties;
/**
- * Plexus invoker implementation, that boots up Plexus DI container. This class expects fully setup ClassWorld via constructor.
+ * Lookup invoker implementation, that boots up DI container.
*
- * @param the options type
- * @param the request type
- * @param the context type
+ * @param The context type.
*/
-public abstract class LookupInvoker<
- O extends Options, R extends InvokerRequest, C extends LookupInvoker.LookupInvokerContext>
- implements Invoker {
-
- /**
- * Exception for intentional exit: No message or anything will be displayed, just the
- * carried exit code will be returned from {@link #invoke(InvokerRequest)} method.
- */
- public static final class ExitException extends InvokerException {
- private final int exitCode;
-
- public ExitException(int exitCode) {
- super("EXIT");
- this.exitCode = exitCode;
- }
- }
-
- @SuppressWarnings("VisibilityModifier")
- public static class LookupInvokerContext<
- O extends Options, R extends InvokerRequest, C extends LookupInvokerContext>
- implements AutoCloseable {
- public final LookupInvoker invoker;
- public final ProtoLookup protoLookup;
- public final R invokerRequest;
- public final Function cwdResolver;
- public final Function installationResolver;
- public final Function userResolver;
- public final Session session;
-
- protected LookupInvokerContext(LookupInvoker invoker, R invokerRequest) {
- this.invoker = invoker;
- this.protoLookup = invoker.protoLookup;
- this.invokerRequest = requireNonNull(invokerRequest);
- this.cwdResolver = s -> invokerRequest.cwd().resolve(s).normalize().toAbsolutePath();
- this.installationResolver = s -> invokerRequest
- .installationDirectory()
- .resolve(s)
- .normalize()
- .toAbsolutePath();
- this.userResolver = s ->
- invokerRequest.userHomeDirectory().resolve(s).normalize().toAbsolutePath();
- this.logger = invokerRequest.parserRequest().logger();
-
- Map user = new HashMap<>(invokerRequest.userProperties());
- user.put("session.rootDirectory", invokerRequest.rootDirectory().toString());
- user.put("session.topDirectory", invokerRequest.topDirectory().toString());
- Map system = new HashMap<>(invokerRequest.systemProperties());
- this.session = ProtoSession.create(user, system);
- }
-
- public Logger logger;
- public ILoggerFactory loggerFactory;
- public Slf4jConfiguration slf4jConfiguration;
- public Slf4jConfiguration.Level loggerLevel;
- public Boolean coloredOutput;
- public Terminal terminal;
- public Consumer writer;
- public ClassLoader currentThreadContextClassLoader;
- public ContainerCapsule containerCapsule;
- public Lookup lookup;
- public SettingsBuilder settingsBuilder;
-
- public boolean interactive;
- public Path localRepositoryPath;
- public Path installationSettingsPath;
- public Path projectSettingsPath;
- public Path userSettingsPath;
- public Settings effectiveSettings;
-
- public final List closeables = new ArrayList<>();
-
- @Override
- public void close() throws InvokerException {
- List causes = null;
- List cs = new ArrayList<>(closeables);
- Collections.reverse(cs);
- for (AutoCloseable c : cs) {
- if (c != null) {
- try {
- c.close();
- } catch (Exception e) {
- if (causes == null) {
- causes = new ArrayList<>();
- }
- causes.add(e);
- }
- }
- }
- if (causes != null) {
- InvokerException exception = new InvokerException("Unable to close context");
- causes.forEach(exception::addSuppressed);
- throw exception;
- }
- }
- }
-
+public abstract class LookupInvoker implements Invoker {
protected final ProtoLookup protoLookup;
public LookupInvoker(ProtoLookup protoLookup) {
@@ -200,19 +97,23 @@ public LookupInvoker(ProtoLookup protoLookup) {
}
@Override
- public int invoke(R invokerRequest) throws InvokerException {
+ public int invoke(InvokerRequest invokerRequest) throws InvokerException {
requireNonNull(invokerRequest);
Properties oldProps = (Properties) System.getProperties().clone();
ClassLoader oldCL = Thread.currentThread().getContextClassLoader();
try (C context = createContext(invokerRequest)) {
try {
- if (context.currentThreadContextClassLoader != null) {
- Thread.currentThread().setContextClassLoader(context.currentThreadContextClassLoader);
+ if (context.containerCapsule != null
+ && context.containerCapsule.currentThreadClassLoader().isPresent()) {
+ Thread.currentThread()
+ .setContextClassLoader(context.containerCapsule
+ .currentThreadClassLoader()
+ .get());
}
return doInvoke(context);
- } catch (ExitException e) {
- return e.exitCode;
+ } catch (InvokerException.ExitException e) {
+ return e.getExitCode();
} catch (Exception e) {
throw handleException(context, e);
}
@@ -238,8 +139,7 @@ protected int doInvoke(C context) throws Exception {
return execute(context);
}
- protected InvokerException handleException(LookupInvokerContext context, Exception e)
- throws InvokerException {
+ protected InvokerException handleException(C context, Exception e) throws InvokerException {
boolean showStackTrace = context.invokerRequest.options().showErrors().orElse(false);
if (showStackTrace) {
context.logger.error(
@@ -255,10 +155,10 @@ protected InvokerException handleException(LookupInvokerContext context
return new InvokerException(e.getMessage(), e);
}
- protected abstract C createContext(R invokerRequest) throws InvokerException;
+ protected abstract C createContext(InvokerRequest invokerRequest) throws InvokerException;
protected void pushProperties(C context) throws Exception {
- R invokerRequest = context.invokerRequest;
+ InvokerRequest invokerRequest = context.invokerRequest;
HashSet sys = new HashSet<>(invokerRequest.systemProperties().keySet());
invokerRequest.userProperties().entrySet().stream()
.filter(k -> !sys.contains(k.getKey()))
@@ -272,7 +172,7 @@ protected void validate(C context) throws Exception {}
protected void prepare(C context) throws Exception {}
protected void configureLogging(C context) throws Exception {
- R invokerRequest = context.invokerRequest;
+ InvokerRequest invokerRequest = context.invokerRequest;
// LOG COLOR
Options mavenOptions = invokerRequest.options();
Map userProperties = invokerRequest.userProperties();
@@ -338,7 +238,7 @@ protected Terminal createTerminal(C context) {
}
protected void doConfigureWithTerminal(C context, Terminal terminal) {
- O options = context.invokerRequest.options();
+ Options options = context.invokerRequest.options();
if (options.rawStreams().isEmpty() || !options.rawStreams().get()) {
MavenSimpleLogger stdout = (MavenSimpleLogger) context.loggerFactory.getLogger("stdout");
MavenSimpleLogger stderr = (MavenSimpleLogger) context.loggerFactory.getLogger("stderr");
@@ -358,7 +258,7 @@ protected Consumer determineWriter(C context) {
}
protected Consumer doDetermineWriter(C context) {
- O options = context.invokerRequest.options();
+ Options options = context.invokerRequest.options();
if (options.logFile().isPresent()) {
Path logFile = context.cwdResolver.apply(options.logFile().get());
try {
@@ -380,7 +280,7 @@ protected Consumer doDetermineWriter(C context) {
}
protected void activateLogging(C context) throws Exception {
- R invokerRequest = context.invokerRequest;
+ InvokerRequest invokerRequest = context.invokerRequest;
Options mavenOptions = invokerRequest.options();
context.slf4jConfiguration.activate();
@@ -412,21 +312,21 @@ protected void activateLogging(C context) throws Exception {
}
protected void helpOrVersionAndMayExit(C context) throws Exception {
- R invokerRequest = context.invokerRequest;
+ InvokerRequest invokerRequest = context.invokerRequest;
if (invokerRequest.options().help().isPresent()) {
Consumer writer = determineWriter(context);
invokerRequest.options().displayHelp(context.invokerRequest.parserRequest(), writer);
- throw new ExitException(0);
+ throw new InvokerException.ExitException(0);
}
if (invokerRequest.options().showVersionAndExit().isPresent()) {
showVersion(context);
- throw new ExitException(0);
+ throw new InvokerException.ExitException(0);
}
}
protected void showVersion(C context) {
Consumer writer = determineWriter(context);
- R invokerRequest = context.invokerRequest;
+ InvokerRequest invokerRequest = context.invokerRequest;
if (invokerRequest.options().quiet().orElse(false)) {
writer.accept(CLIReportingUtils.showVersionMinimal());
} else if (invokerRequest.options().verbose().orElse(false)) {
@@ -466,10 +366,9 @@ protected void preCommands(C context) throws Exception {
}
protected void container(C context) throws Exception {
- context.containerCapsule = createContainerCapsuleFactory().createContainerCapsule(context);
+ context.containerCapsule = createContainerCapsuleFactory().createContainerCapsule(this, context);
context.closeables.add(context.containerCapsule);
context.lookup = context.containerCapsule.getLookup();
- context.settingsBuilder = context.lookup.lookup(SettingsBuilder.class);
// refresh logger in case container got customized by spy
org.slf4j.Logger l = context.loggerFactory.getLogger(this.getClass().getName());
@@ -478,7 +377,7 @@ protected void container(C context) throws Exception {
.log(message);
}
- protected ContainerCapsuleFactory createContainerCapsuleFactory() {
+ protected ContainerCapsuleFactory createContainerCapsuleFactory() {
return new PlexusContainerCapsuleFactory<>();
}
@@ -487,7 +386,7 @@ protected void lookup(C context) throws Exception {}
protected void init(C context) throws Exception {}
protected void postCommands(C context) throws Exception {
- R invokerRequest = context.invokerRequest;
+ InvokerRequest invokerRequest = context.invokerRequest;
Logger logger = context.logger;
if (invokerRequest.options().showErrors().orElse(false)) {
logger.info("Error stacktraces are turned on.");
@@ -513,7 +412,7 @@ protected void postCommands(C context) throws Exception {
}
protected void settings(C context) throws Exception {
- settings(context, context.settingsBuilder);
+ settings(context, context.lookup.lookup(SettingsBuilder.class));
}
protected void settings(C context, SettingsBuilder settingsBuilder) throws Exception {
diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/PlexusContainerCapsule.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/PlexusContainerCapsule.java
index bd9b9507c8f4..05d53272d309 100644
--- a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/PlexusContainerCapsule.java
+++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/PlexusContainerCapsule.java
@@ -18,6 +18,8 @@
*/
package org.apache.maven.cling.invoker;
+import java.util.Optional;
+
import org.apache.maven.api.services.Lookup;
import org.apache.maven.internal.impl.DefaultLookup;
import org.codehaus.plexus.PlexusContainer;
@@ -43,6 +45,11 @@ public Lookup getLookup() {
return lookup;
}
+ @Override
+ public Optional currentThreadClassLoader() {
+ return Optional.of(plexusContainer.getContainerRealm());
+ }
+
@Override
public void close() {
try {
diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/PlexusContainerCapsuleFactory.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/PlexusContainerCapsuleFactory.java
index 3e70d4c9714f..bfcef512825d 100644
--- a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/PlexusContainerCapsuleFactory.java
+++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/PlexusContainerCapsuleFactory.java
@@ -33,7 +33,6 @@
import org.apache.maven.api.cli.InvokerException;
import org.apache.maven.api.cli.InvokerRequest;
import org.apache.maven.api.cli.Logger;
-import org.apache.maven.api.cli.Options;
import org.apache.maven.api.cli.extensions.CoreExtension;
import org.apache.maven.api.services.MessageBuilderFactory;
import org.apache.maven.api.services.SettingsBuilder;
@@ -65,28 +64,26 @@
/**
* Container capsule backed by Plexus Container.
*
- * @param the options type
- * @param the invoker request type
- * @param the invoker context type
+ * @param The context type.
*/
-public class PlexusContainerCapsuleFactory<
- O extends Options, R extends InvokerRequest, C extends LookupInvoker.LookupInvokerContext>
- implements ContainerCapsuleFactory {
+public class PlexusContainerCapsuleFactory implements ContainerCapsuleFactory {
@Override
- public ContainerCapsule createContainerCapsule(C context) throws InvokerException {
+ public ContainerCapsule createContainerCapsule(LookupInvoker invoker, C context) throws InvokerException {
try {
- return new PlexusContainerCapsule(Thread.currentThread().getContextClassLoader(), container(context));
+ return new PlexusContainerCapsule(
+ Thread.currentThread().getContextClassLoader(), container(invoker, context));
} catch (Exception e) {
throw new InvokerException("Failed to create plexus container capsule", e);
}
}
- protected PlexusContainer container(C context) throws Exception {
- ClassWorld classWorld = context.protoLookup.lookup(ClassWorld.class);
+ protected PlexusContainer container(LookupInvoker invoker, C context) throws Exception {
+ ClassWorld classWorld = invoker.protoLookup.lookup(ClassWorld.class);
ClassRealm coreRealm = classWorld.getClassRealm("plexus.core");
List extClassPath = parseExtClasspath(context);
CoreExtensionEntry coreEntry = CoreExtensionEntry.discoverFrom(coreRealm);
- List extensions = loadCoreExtensions(context, coreRealm, coreEntry.getExportedArtifacts());
+ List extensions =
+ loadCoreExtensions(invoker, context, coreRealm, coreEntry.getExportedArtifacts());
ClassRealm containerRealm =
setupContainerRealm(context.logger, classWorld, coreRealm, extClassPath, extensions);
ContainerConfiguration cc = new DefaultContainerConfiguration()
@@ -108,11 +105,10 @@ protected PlexusContainer container(C context) throws Exception {
// NOTE: To avoid inconsistencies, we'll use the TCCL exclusively for lookups
container.setLookupRealm(null);
- context.currentThreadContextClassLoader = container.getContainerRealm();
Thread.currentThread().setContextClassLoader(container.getContainerRealm());
container.setLoggerManager(createLoggerManager());
- R invokerRequest = context.invokerRequest;
+ InvokerRequest invokerRequest = context.invokerRequest;
Function extensionSource = expression -> {
String value = invokerRequest.userProperties().get(expression);
if (value == null) {
@@ -193,7 +189,7 @@ protected void customizeContainerConfiguration(C context, ContainerConfiguration
protected void customizeContainer(C context, PlexusContainer container) throws Exception {}
protected List parseExtClasspath(C context) throws Exception {
- R invokerRequest = context.invokerRequest;
+ InvokerRequest invokerRequest = context.invokerRequest;
String extClassPath = invokerRequest.userProperties().get(Constants.MAVEN_EXT_CLASS_PATH);
if (extClassPath == null) {
extClassPath = invokerRequest.systemProperties().get(Constants.MAVEN_EXT_CLASS_PATH);
@@ -255,8 +251,9 @@ protected ClassRealm setupContainerRealm(
}
protected List loadCoreExtensions(
- C context, ClassRealm containerRealm, Set providedArtifacts) throws Exception {
- R invokerRequest = context.invokerRequest;
+ LookupInvoker invoker, C context, ClassRealm containerRealm, Set providedArtifacts)
+ throws Exception {
+ InvokerRequest invokerRequest = context.invokerRequest;
if (invokerRequest.coreExtensions().isEmpty()
|| invokerRequest.coreExtensions().get().isEmpty()) {
return Collections.emptyList();
@@ -286,10 +283,10 @@ protected void configure() {
container.getLoggerManager().setThresholds(toPlexusLoggingLevel(context.loggerLevel));
Thread.currentThread().setContextClassLoader(container.getContainerRealm());
- context.invoker.settings(context, container.lookup(SettingsBuilder.class));
+ invoker.settings(context, container.lookup(SettingsBuilder.class));
MavenExecutionRequest mer = new DefaultMavenExecutionRequest();
- context.invoker.populateRequest(context, mer);
+ invoker.populateRequest(context, mer);
mer = container.lookup(MavenExecutionRequestPopulator.class).populateDefaults(mer);
return Collections.unmodifiableList(container
.lookup(BootstrapCoreExtensionManager.class)
diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/ProtoLogger.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/ProtoLogger.java
index 0f9e6dd252aa..7c795e8d809a 100644
--- a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/ProtoLogger.java
+++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/ProtoLogger.java
@@ -61,7 +61,7 @@ private PrintStream toPsOrDef(OutputStream outputStream, PrintStream def) {
public void log(Level level, String message, Throwable error) {
PrintWriter pw = level == Level.ERROR ? err : level == Level.WARN ? out : null;
if (pw != null) {
- pw.print(level.name() + " " + message);
+ pw.println(level.name() + " " + message);
if (error != null) {
error.printStackTrace(pw);
}
diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/DefaultMavenParser.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/DefaultMavenParser.java
deleted file mode 100644
index 78db2be0848b..000000000000
--- a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/DefaultMavenParser.java
+++ /dev/null
@@ -1,62 +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.invoker.mvn;
-
-import java.util.List;
-
-import org.apache.commons.cli.ParseException;
-import org.apache.maven.api.cli.ParserException;
-import org.apache.maven.api.cli.mvn.MavenInvokerRequest;
-import org.apache.maven.api.cli.mvn.MavenOptions;
-import org.apache.maven.api.cli.mvn.MavenParser;
-
-public class DefaultMavenParser extends BaseMavenParser>
- implements MavenParser> {
- @Override
- protected DefaultMavenInvokerRequest getInvokerRequest(LocalContext context) {
- return new DefaultMavenInvokerRequest<>(
- context.parserRequest,
- context.cwd,
- context.installationDirectory,
- context.userHomeDirectory,
- context.userProperties,
- context.systemProperties,
- context.topDirectory,
- context.rootDirectory,
- context.parserRequest.in(),
- context.parserRequest.out(),
- context.parserRequest.err(),
- context.extensions,
- (MavenOptions) context.options);
- }
-
- @Override
- protected MavenOptions parseArgs(String source, List args) throws ParserException {
- try {
- return CommonsCliMavenOptions.parse(source, args.toArray(new String[0]));
- } catch (ParseException e) {
- throw new ParserException("Failed to parse source " + source + ": " + e.getMessage(), e.getCause());
- }
- }
-
- @Override
- protected MavenOptions assembleOptions(List parsedOptions) {
- return LayeredMavenOptions.layerMavenOptions(parsedOptions);
- }
-}
diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/MavenContext.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/MavenContext.java
new file mode 100644
index 000000000000..26d9c2b512ce
--- /dev/null
+++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/MavenContext.java
@@ -0,0 +1,42 @@
+/*
+ * 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.invoker.mvn;
+
+import org.apache.maven.Maven;
+import org.apache.maven.api.cli.InvokerRequest;
+import org.apache.maven.api.services.model.ModelProcessor;
+import org.apache.maven.cling.invoker.LookupContext;
+import org.apache.maven.eventspy.internal.EventSpyDispatcher;
+import org.apache.maven.execution.MavenExecutionRequest;
+import org.apache.maven.execution.MavenExecutionRequestPopulator;
+import org.apache.maven.logging.BuildEventListener;
+
+@SuppressWarnings("VisibilityModifier")
+public class MavenContext extends LookupContext {
+ public MavenContext(InvokerRequest invokerRequest) {
+ super(invokerRequest);
+ }
+
+ public BuildEventListener buildEventListener;
+ public MavenExecutionRequest mavenExecutionRequest;
+ public EventSpyDispatcher eventSpyDispatcher;
+ public MavenExecutionRequestPopulator mavenExecutionRequestPopulator;
+ public ModelProcessor modelProcessor;
+ public Maven maven;
+}
diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/DefaultMavenInvoker.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/MavenInvoker.java
similarity index 90%
rename from impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/DefaultMavenInvoker.java
rename to impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/MavenInvoker.java
index 665256d1277f..9fdb40c1480b 100644
--- a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/DefaultMavenInvoker.java
+++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/MavenInvoker.java
@@ -34,9 +34,8 @@
import org.apache.maven.InternalErrorException;
import org.apache.maven.Maven;
import org.apache.maven.api.Constants;
+import org.apache.maven.api.cli.InvokerRequest;
import org.apache.maven.api.cli.Logger;
-import org.apache.maven.api.cli.mvn.MavenInvoker;
-import org.apache.maven.api.cli.mvn.MavenInvokerRequest;
import org.apache.maven.api.cli.mvn.MavenOptions;
import org.apache.maven.api.services.BuilderProblem;
import org.apache.maven.api.services.SettingsBuilderRequest;
@@ -77,32 +76,13 @@
import static java.util.Comparator.comparing;
import static org.apache.maven.cling.invoker.Utils.toProperties;
-public abstract class DefaultMavenInvoker<
- O extends MavenOptions,
- R extends MavenInvokerRequest,
- C extends DefaultMavenInvoker.MavenContext>
- extends LookupInvoker implements MavenInvoker {
-
- @SuppressWarnings("VisibilityModifier")
- public static class MavenContext<
- O extends MavenOptions,
- R extends MavenInvokerRequest,
- C extends DefaultMavenInvoker.MavenContext>
- extends LookupInvokerContext {
- protected MavenContext(DefaultMavenInvoker invoker, R invokerRequest) {
- super(invoker, invokerRequest);
- }
-
- public BuildEventListener buildEventListener;
- public MavenExecutionRequest mavenExecutionRequest;
- public EventSpyDispatcher eventSpyDispatcher;
- public MavenExecutionRequestPopulator mavenExecutionRequestPopulator;
- public ToolchainsBuilder toolchainsBuilder;
- public ModelProcessor modelProcessor;
- public Maven maven;
- }
-
- public DefaultMavenInvoker(ProtoLookup protoLookup) {
+/**
+ * The "local" Maven invoker, that expects whole Maven on classpath and invokes it.
+ *
+ * @param The context type.
+ */
+public abstract class MavenInvoker extends LookupInvoker {
+ public MavenInvoker(ProtoLookup protoLookup) {
super(protoLookup);
}
@@ -136,14 +116,13 @@ protected void prepare(C context) throws Exception {
protected void lookup(C context) throws Exception {
context.eventSpyDispatcher = context.lookup.lookup(EventSpyDispatcher.class);
context.mavenExecutionRequestPopulator = context.lookup.lookup(MavenExecutionRequestPopulator.class);
- context.toolchainsBuilder = context.lookup.lookup(ToolchainsBuilder.class);
context.modelProcessor = context.lookup.lookup(ModelProcessor.class);
context.maven = context.lookup.lookup(Maven.class);
}
@Override
protected void init(C context) throws Exception {
- MavenInvokerRequest invokerRequest = context.invokerRequest;
+ InvokerRequest invokerRequest = context.invokerRequest;
Map data = new HashMap<>();
data.put("plexus", context.lookup.lookup(PlexusContainer.class));
data.put("workingDirectory", invokerRequest.cwd().toString());
@@ -157,11 +136,12 @@ protected void init(C context) throws Exception {
protected void postCommands(C context) throws Exception {
super.postCommands(context);
- R invokerRequest = context.invokerRequest;
+ InvokerRequest invokerRequest = context.invokerRequest;
+ MavenOptions options = (MavenOptions) invokerRequest.options();
Logger logger = context.logger;
- if (invokerRequest.options().relaxedChecksums().orElse(false)) {
+ if (options.relaxedChecksums().orElse(false)) {
logger.info("Disabling strict checksum verification on all artifact downloads.");
- } else if (invokerRequest.options().strictChecksums().orElse(false)) {
+ } else if (options.strictChecksums().orElse(false)) {
logger.info("Enabling strict checksum verification on all artifact downloads.");
}
}
@@ -260,7 +240,8 @@ protected void toolchains(C context) throws Exception {
context.logger.debug("Reading installation toolchains from '" + installationToolchainsFile + "'");
context.logger.debug("Reading user toolchains from '" + userToolchainsFile + "'");
- ToolchainsBuilderResult toolchainsResult = context.toolchainsBuilder.build(toolchainsRequest);
+ ToolchainsBuilderResult toolchainsResult =
+ context.lookup.lookup(ToolchainsBuilder.class).build(toolchainsRequest);
context.eventSpyDispatcher.onEvent(toolchainsResult);
@@ -290,7 +271,7 @@ protected void populateRequest(C context, MavenExecutionRequest request) throws
request.setRootDirectory(context.invokerRequest.topDirectory());
}
- MavenOptions options = context.invokerRequest.options();
+ MavenOptions options = (MavenOptions) context.invokerRequest.options();
request.setNoSnapshotUpdates(options.suppressSnapshotUpdates().orElse(false));
request.setGoals(options.goals().orElse(List.of()));
request.setReactorFailureBehavior(determineReactorFailureBehaviour(context));
@@ -346,9 +327,9 @@ protected void populateRequest(C context, MavenExecutionRequest request) throws
// parameters but this is sufficient for now. Ultimately we want components like Builders to provide a way to
// extend the command line to accept its own configuration parameters.
//
- if (context.invokerRequest.options().threads().isPresent()) {
- int degreeOfConcurrency = calculateDegreeOfConcurrency(
- context.invokerRequest.options().threads().get());
+ if (options.threads().isPresent()) {
+ int degreeOfConcurrency =
+ calculateDegreeOfConcurrency(options.threads().get());
if (degreeOfConcurrency > 1) {
request.setBuilderId("multithreaded");
request.setDegreeOfConcurrency(degreeOfConcurrency);
@@ -358,16 +339,16 @@ protected void populateRequest(C context, MavenExecutionRequest request) throws
//
// Allow the builder to be overridden by the user if requested. The builders are now pluggable.
//
- if (context.invokerRequest.options().builder().isPresent()) {
- request.setBuilderId(context.invokerRequest.options().builder().get());
+ if (options.builder().isPresent()) {
+ request.setBuilderId(options.builder().get());
}
}
protected Path determinePom(C context) {
Path current = context.invokerRequest.cwd();
- if (context.invokerRequest.options().alternatePomFile().isPresent()) {
- current = context.cwdResolver.apply(
- context.invokerRequest.options().alternatePomFile().get());
+ MavenOptions options = (MavenOptions) context.invokerRequest.options();
+ if (options.alternatePomFile().isPresent()) {
+ current = context.cwdResolver.apply(options.alternatePomFile().get());
}
if (context.modelProcessor != null) {
return context.modelProcessor.locateExistingPom(current);
@@ -377,7 +358,7 @@ protected Path determinePom(C context) {
}
protected String determineReactorFailureBehaviour(C context) {
- MavenOptions mavenOptions = context.invokerRequest.options();
+ MavenOptions mavenOptions = (MavenOptions) context.invokerRequest.options();
if (mavenOptions.failFast().isPresent()) {
return MavenExecutionRequest.REACTOR_FAIL_FAST;
} else if (mavenOptions.failAtEnd().isPresent()) {
@@ -390,7 +371,7 @@ protected String determineReactorFailureBehaviour(C context) {
}
protected String determineGlobalChecksumPolicy(C context) {
- MavenOptions mavenOptions = context.invokerRequest.options();
+ MavenOptions mavenOptions = (MavenOptions) context.invokerRequest.options();
if (mavenOptions.strictChecksums().orElse(false)) {
return MavenExecutionRequest.CHECKSUM_POLICY_FAIL;
} else if (mavenOptions.relaxedChecksums().orElse(false)) {
@@ -415,7 +396,7 @@ protected TransferListener determineTransferListener(C context, boolean noTransf
}
protected String determineMakeBehavior(C context) {
- MavenOptions mavenOptions = context.invokerRequest.options();
+ MavenOptions mavenOptions = (MavenOptions) context.invokerRequest.options();
if (mavenOptions.alsoMake().isPresent()
&& mavenOptions.alsoMakeDependents().isEmpty()) {
return MavenExecutionRequest.REACTOR_MAKE_UPSTREAM;
@@ -431,7 +412,7 @@ protected String determineMakeBehavior(C context) {
}
protected void performProjectActivation(C context, ProjectActivation projectActivation) {
- MavenOptions mavenOptions = context.invokerRequest.options();
+ MavenOptions mavenOptions = (MavenOptions) context.invokerRequest.options();
if (mavenOptions.projects().isPresent()
&& !mavenOptions.projects().get().isEmpty()) {
List optionValues = mavenOptions.projects().get();
@@ -459,7 +440,7 @@ protected void performProjectActivation(C context, ProjectActivation projectActi
}
protected void performProfileActivation(C context, ProfileActivation profileActivation) {
- MavenOptions mavenOptions = context.invokerRequest.options();
+ MavenOptions mavenOptions = (MavenOptions) context.invokerRequest.options();
if (mavenOptions.activatedProfiles().isPresent()
&& !mavenOptions.activatedProfiles().get().isEmpty()) {
List optionValues = mavenOptions.activatedProfiles().get();
@@ -549,7 +530,7 @@ protected int doExecute(C context) throws Exception {
}
}
- if (context.invokerRequest.options().failNever().orElse(false)) {
+ if (((MavenOptions) context.invokerRequest.options()).failNever().orElse(false)) {
context.logger.info("Build failures were ignored.");
return 0;
} else {
diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/DefaultMavenInvokerRequest.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/MavenInvokerRequest.java
similarity index 85%
rename from impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/DefaultMavenInvokerRequest.java
rename to impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/MavenInvokerRequest.java
index 1ae5cf7b7729..788c67e40f8e 100644
--- a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/DefaultMavenInvokerRequest.java
+++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/MavenInvokerRequest.java
@@ -27,7 +27,6 @@
import org.apache.maven.api.annotations.Nonnull;
import org.apache.maven.api.cli.ParserRequest;
import org.apache.maven.api.cli.extensions.CoreExtension;
-import org.apache.maven.api.cli.mvn.MavenInvokerRequest;
import org.apache.maven.api.cli.mvn.MavenOptions;
import org.apache.maven.cling.invoker.BaseInvokerRequest;
@@ -35,15 +34,12 @@
/**
* Maven execution request.
- *
- * @param the options type this request carries
*/
-public class DefaultMavenInvokerRequest extends BaseInvokerRequest
- implements MavenInvokerRequest {
- private final O options;
+public class MavenInvokerRequest extends BaseInvokerRequest {
+ private final MavenOptions options;
@SuppressWarnings("ParameterNumber")
- public DefaultMavenInvokerRequest(
+ public MavenInvokerRequest(
ParserRequest parserRequest,
Path cwd,
Path installationDirectory,
@@ -56,7 +52,8 @@ public DefaultMavenInvokerRequest(
OutputStream out,
OutputStream err,
List coreExtensions,
- O options) {
+ List jvmArguments,
+ MavenOptions options) {
super(
parserRequest,
cwd,
@@ -69,7 +66,8 @@ public DefaultMavenInvokerRequest(
in,
out,
err,
- coreExtensions);
+ coreExtensions,
+ jvmArguments);
this.options = requireNonNull(options);
}
@@ -77,7 +75,7 @@ public DefaultMavenInvokerRequest(
* The mandatory Maven options.
*/
@Nonnull
- public O options() {
+ public MavenOptions options() {
return options;
}
}
diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/BaseMavenParser.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/MavenParser.java
similarity index 55%
rename from impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/BaseMavenParser.java
rename to impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/MavenParser.java
index d2b5752f0cb8..944d0a94f942 100644
--- a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/BaseMavenParser.java
+++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/MavenParser.java
@@ -26,19 +26,17 @@
import java.util.List;
import java.util.stream.Stream;
+import org.apache.commons.cli.ParseException;
import org.apache.maven.api.cli.Options;
import org.apache.maven.api.cli.ParserException;
-import org.apache.maven.api.cli.mvn.MavenInvokerRequest;
import org.apache.maven.api.cli.mvn.MavenOptions;
-import org.apache.maven.api.cli.mvn.MavenParser;
import org.apache.maven.cling.invoker.BaseParser;
-public abstract class BaseMavenParser> extends BaseParser
- implements MavenParser {
+public class MavenParser extends BaseParser {
@Override
- protected List parseCliOptions(LocalContext context) throws ParserException, IOException {
- ArrayList result = new ArrayList<>();
+ protected List parseCliOptions(LocalContext context) throws ParserException, IOException {
+ ArrayList result = new ArrayList<>();
// CLI args
result.add(parseMavenCliOptions(context.parserRequest.args()));
// maven.config; if exists
@@ -49,15 +47,15 @@ protected List parseCliOptions(LocalContext context) throws ParserException,
return result;
}
- protected O parseMavenCliOptions(List args) throws ParserException {
+ protected MavenOptions parseMavenCliOptions(List args) throws ParserException {
return parseArgs(Options.SOURCE_CLI, args);
}
- protected O parseMavenConfigOptions(Path configFile) throws ParserException, IOException {
+ protected MavenOptions parseMavenConfigOptions(Path configFile) throws ParserException, IOException {
try (Stream lines = Files.lines(configFile, Charset.defaultCharset())) {
List args =
lines.filter(arg -> !arg.isEmpty() && !arg.startsWith("#")).toList();
- O options = parseArgs("maven.config", args);
+ MavenOptions options = parseArgs("maven.config", args);
if (options.goals().isPresent()) {
// This file can only contain options, not args (goals or phases)
throw new ParserException("Unrecognized maven.config file entries: "
@@ -67,5 +65,36 @@ protected O parseMavenConfigOptions(Path configFile) throws ParserException, IOE
}
}
- protected abstract O parseArgs(String source, List args) throws ParserException;
+ protected MavenOptions parseArgs(String source, List args) throws ParserException {
+ try {
+ return CommonsCliMavenOptions.parse(source, args.toArray(new String[0]));
+ } catch (ParseException e) {
+ throw new ParserException("Failed to parse source " + source + ": " + e.getMessage(), e.getCause());
+ }
+ }
+
+ @Override
+ protected MavenInvokerRequest getInvokerRequest(LocalContext context) {
+ return new MavenInvokerRequest(
+ context.parserRequest,
+ context.cwd,
+ context.installationDirectory,
+ context.userHomeDirectory,
+ context.userProperties,
+ context.systemProperties,
+ context.topDirectory,
+ context.rootDirectory,
+ context.parserRequest.in(),
+ context.parserRequest.out(),
+ context.parserRequest.err(),
+ context.extensions,
+ getJvmArguments(context.rootDirectory),
+ (MavenOptions) context.options);
+ }
+
+ @Override
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ protected MavenOptions assembleOptions(List parsedOptions) {
+ return LayeredMavenOptions.layerMavenOptions((List) parsedOptions);
+ }
}
diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/embedded/EmbeddedMavenExecutor.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/embedded/EmbeddedMavenExecutor.java
new file mode 100644
index 000000000000..ff34b8c4896e
--- /dev/null
+++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/embedded/EmbeddedMavenExecutor.java
@@ -0,0 +1,267 @@
+/*
+ * 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.invoker.mvn.embedded;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+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.Collections;
+import java.util.List;
+import java.util.Properties;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Function;
+import java.util.stream.Stream;
+
+import org.apache.maven.api.cli.Executor;
+import org.apache.maven.api.cli.ExecutorException;
+import org.apache.maven.api.cli.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 class world with 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 {
+ protected static final class Context {
+ private final Properties properties;
+ private final URLClassLoader bootClassLoader;
+ private final String version;
+ private final Object classWorld;
+ private final ClassLoader tccl;
+ private final Function exec;
+
+ public Context(
+ Properties properties,
+ URLClassLoader bootClassLoader,
+ String version,
+ Object classWorld,
+ ClassLoader tccl,
+ Function exec) {
+ this.properties = properties;
+ this.bootClassLoader = bootClassLoader;
+ this.version = version;
+ this.classWorld = classWorld;
+ this.tccl = tccl;
+ this.exec = exec;
+ }
+ }
+
+ private final Properties originalProperties;
+ private final ClassLoader originalClassLoader;
+ private final ConcurrentHashMap contexts;
+
+ public EmbeddedMavenExecutor() {
+ this.originalClassLoader = Thread.currentThread().getContextClassLoader();
+ this.contexts = new ConcurrentHashMap<>();
+ this.originalProperties = System.getProperties();
+ }
+
+ @Override
+ public int execute(ExecutorRequest executorRequest) throws ExecutorException {
+ requireNonNull(executorRequest);
+ validate(executorRequest);
+ Context context = mayCreate(executorRequest);
+
+ System.setProperties(context.properties);
+ Thread.currentThread().setContextClassLoader(context.tccl);
+ try {
+ return context.exec.apply(executorRequest);
+ } catch (Exception e) {
+ throw new ExecutorException("Failed to execute", e);
+ } finally {
+ Thread.currentThread().setContextClassLoader(originalClassLoader);
+ System.setProperties(originalProperties);
+ }
+ }
+
+ protected Context mayCreate(ExecutorRequest executorRequest) {
+ Path installation = executorRequest.installationDirectory();
+ if (!Files.isDirectory(installation)) {
+ throw new IllegalArgumentException("Installation directory must point to existing directory");
+ }
+ return contexts.computeIfAbsent(installation, k -> {
+ Path mavenHome = installation.toAbsolutePath().normalize();
+ 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");
+ }
+
+ Properties properties = new Properties();
+ properties.putAll(System.getProperties());
+ properties.put(
+ "user.dir",
+ executorRequest.cwd().toAbsolutePath().normalize().toString());
+ properties.put(
+ "maven.multiModuleProjectDirectory",
+ executorRequest.cwd().toAbsolutePath().normalize().toString());
+ properties.put(
+ "user.home",
+ executorRequest
+ .userHomeDirectory()
+ .toAbsolutePath()
+ .normalize()
+ .toString());
+ properties.put("maven.home", mavenHome.toString());
+ properties.put("maven.mainClass", "org.apache.maven.cling.MavenCling");
+ properties.put(
+ "library.jline.path", mavenHome.resolve("lib/jline-native").toString());
+
+ 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);
+ Class> cliClass =
+ (Class>) launcherClass.getMethod("getMainClass").invoke(launcher);
+ String version = getMavenVersion(cliClass.getClassLoader());
+ Function exec;
+
+ if (version.startsWith("3.")) {
+ // 3.x
+ 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);
+ exec = r -> {
+ try {
+ return (int) doMain.invoke(mavenCli, new Object[] {
+ r.parserRequest().args().toArray(new String[0]),
+ r.cwd().toString(),
+ null,
+ null
+ });
+ } catch (Exception e) {
+ throw new ExecutorException("Failed to execute", e);
+ }
+ };
+ } else {
+ // assume 4.x
+ Method mainMethod = cliClass.getMethod("main", String[].class, classWorld.getClass());
+ exec = r -> {
+ try {
+ return (int) mainMethod.invoke(
+ null, r.parserRequest().args().toArray(new String[0]), classWorld);
+ } catch (Exception e) {
+ throw new ExecutorException("Failed to execute", e);
+ }
+ };
+ }
+
+ return new Context(properties, bootClassLoader, version, classWorld, cliClass.getClassLoader(), exec);
+ } catch (Exception e) {
+ throw new ExecutorException("Failed to create executor", e);
+ } finally {
+ Thread.currentThread().setContextClassLoader(originalClassLoader);
+ System.setProperties(originalProperties);
+ }
+ });
+ }
+
+ @Override
+ public void close() throws ExecutorException {
+ 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 Exception {
+ Thread.currentThread().setContextClassLoader(context.bootClassLoader);
+ try {
+ try {
+ ((Closeable) context.classWorld).close();
+ } finally {
+ context.bootClassLoader.close();
+ }
+ } 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());
+ }
+
+ public String getMavenVersion(ClassLoader classLoader) throws IOException {
+ Properties props = new Properties();
+ try (InputStream is =
+ classLoader.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";
+ }
+ }
+}
diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/forked/DefaultForkedMavenInvokerRequest.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/forked/DefaultForkedMavenInvokerRequest.java
deleted file mode 100644
index d62c2b77b4e2..000000000000
--- a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/forked/DefaultForkedMavenInvokerRequest.java
+++ /dev/null
@@ -1,78 +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.invoker.mvn.forked;
-
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.file.Path;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-
-import org.apache.maven.api.cli.ParserRequest;
-import org.apache.maven.api.cli.extensions.CoreExtension;
-import org.apache.maven.api.cli.mvn.MavenOptions;
-import org.apache.maven.api.cli.mvn.forked.ForkedMavenInvokerRequest;
-import org.apache.maven.cling.invoker.mvn.DefaultMavenInvokerRequest;
-
-/**
- * Maven execution request.
- */
-public class DefaultForkedMavenInvokerRequest extends DefaultMavenInvokerRequest
- implements ForkedMavenInvokerRequest {
- private final List jvmArguments;
-
- @SuppressWarnings("ParameterNumber")
- public DefaultForkedMavenInvokerRequest(
- ParserRequest parserRequest,
- Path cwd,
- Path installationDirectory,
- Path userHomeDirectory,
- Map userProperties,
- Map systemProperties,
- Path topDirectory,
- Path rootDirectory,
- InputStream in,
- OutputStream out,
- OutputStream err,
- List coreExtensions,
- List jvmArguments,
- MavenOptions options) {
- super(
- parserRequest,
- cwd,
- installationDirectory,
- userHomeDirectory,
- userProperties,
- systemProperties,
- topDirectory,
- rootDirectory,
- in,
- out,
- err,
- coreExtensions,
- options);
- this.jvmArguments = jvmArguments;
- }
-
- @Override
- public Optional> jvmArguments() {
- return Optional.ofNullable(jvmArguments);
- }
-}
diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/forked/DefaultForkedMavenParser.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/forked/DefaultForkedMavenParser.java
deleted file mode 100644
index c46164e822b5..000000000000
--- a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/forked/DefaultForkedMavenParser.java
+++ /dev/null
@@ -1,96 +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.invoker.mvn.forked;
-
-import java.io.IOException;
-import java.io.UncheckedIOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.Arrays;
-import java.util.List;
-import java.util.stream.Collectors;
-
-import org.apache.commons.cli.ParseException;
-import org.apache.maven.api.cli.ParserException;
-import org.apache.maven.api.cli.mvn.MavenOptions;
-import org.apache.maven.api.cli.mvn.forked.ForkedMavenInvokerRequest;
-import org.apache.maven.api.cli.mvn.forked.ForkedMavenParser;
-import org.apache.maven.cling.invoker.mvn.BaseMavenParser;
-import org.apache.maven.cling.invoker.mvn.CommonsCliMavenOptions;
-import org.apache.maven.cling.invoker.mvn.LayeredMavenOptions;
-
-/**
- * Forked invoker that invokes Maven in a child process.
- */
-public class DefaultForkedMavenParser extends BaseMavenParser
- implements ForkedMavenParser {
-
- @SuppressWarnings("ParameterNumber")
- @Override
- protected ForkedMavenInvokerRequest getInvokerRequest(LocalContext context) {
- return new DefaultForkedMavenInvokerRequest(
- context.parserRequest,
- context.cwd,
- context.installationDirectory,
- context.userHomeDirectory,
- context.userProperties,
- context.systemProperties,
- context.topDirectory,
- context.rootDirectory,
- context.parserRequest.in(),
- context.parserRequest.out(),
- context.parserRequest.err(),
- context.extensions,
- getJvmArguments(context.rootDirectory),
- (MavenOptions) context.options);
- }
-
- protected List getJvmArguments(Path rootDirectory) {
- if (rootDirectory != null) {
- Path jvmConfig = rootDirectory.resolve(".mvn/jvm.config");
- if (Files.exists(jvmConfig)) {
- try {
- return Files.readAllLines(jvmConfig).stream()
- .filter(l -> !l.isBlank() && !l.startsWith("#"))
- .flatMap(l -> Arrays.stream(l.split(" ")))
- .collect(Collectors.toList());
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
- }
- return null;
- }
-
- // TODO: same is in DefaultMavenParser!!! (duplication)
- @Override
- protected MavenOptions parseArgs(String source, List args) throws ParserException {
- try {
- return CommonsCliMavenOptions.parse(source, args.toArray(new String[0]));
- } catch (ParseException e) {
- throw new ParserException("Failed to parse source " + source, e.getCause());
- }
- }
-
- // TODO: same is in DefaultMavenParser!!! (duplication)
- @Override
- protected MavenOptions assembleOptions(List parsedOptions) {
- return LayeredMavenOptions.layerMavenOptions(parsedOptions);
- }
-}
diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/forked/ForkedMavenExecutor.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/forked/ForkedMavenExecutor.java
new file mode 100644
index 000000000000..b8e25b84dfb4
--- /dev/null
+++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/forked/ForkedMavenExecutor.java
@@ -0,0 +1,73 @@
+/*
+ * 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.invoker.mvn.forked;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+import org.apache.maven.api.cli.Executor;
+import org.apache.maven.api.cli.ExecutorException;
+import org.apache.maven.api.cli.ExecutorRequest;
+import org.apache.maven.internal.impl.model.profile.Os;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * Forked executor implementation, that spawns a subprocess with Maven from the installation directory.
+ */
+public class ForkedMavenExecutor implements Executor {
+ @Override
+ public int execute(ExecutorRequest executorRequest) throws ExecutorException {
+ requireNonNull(executorRequest);
+ validate(executorRequest);
+
+ ArrayList cmdAndArguments = new ArrayList<>();
+ cmdAndArguments.add(executorRequest
+ .installationDirectory()
+ .resolve("bin")
+ .resolve(
+ Os.IS_WINDOWS
+ ? executorRequest.parserRequest().command() + ".cmd"
+ : executorRequest.parserRequest().command())
+ .toString());
+
+ cmdAndArguments.addAll(executorRequest.parserRequest().args());
+
+ try {
+ ProcessBuilder pb = new ProcessBuilder()
+ .directory(executorRequest.cwd().toFile())
+ .command(cmdAndArguments);
+
+ if (executorRequest.jvmArguments().isPresent()) {
+ pb.environment()
+ .put(
+ "MAVEN_OPTS",
+ String.join(" ", executorRequest.jvmArguments().get()));
+ }
+
+ return pb.start().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 void validate(ExecutorRequest executorRequest) throws ExecutorException {}
+}
diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/forked/DefaultForkedMavenInvoker.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/forked/ForkedMavenInvoker.java
similarity index 94%
rename from impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/forked/DefaultForkedMavenInvoker.java
rename to impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/forked/ForkedMavenInvoker.java
index 647c4a44d9a4..29ad880a2f68 100644
--- a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/forked/DefaultForkedMavenInvoker.java
+++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/forked/ForkedMavenInvoker.java
@@ -23,21 +23,21 @@
import java.util.Collections;
import java.util.Map;
+import org.apache.maven.api.cli.Invoker;
import org.apache.maven.api.cli.InvokerException;
+import org.apache.maven.api.cli.InvokerRequest;
import org.apache.maven.api.cli.mvn.MavenOptions;
-import org.apache.maven.api.cli.mvn.forked.ForkedMavenInvoker;
-import org.apache.maven.api.cli.mvn.forked.ForkedMavenInvokerRequest;
import org.apache.maven.internal.impl.model.profile.Os;
import static java.util.Objects.requireNonNull;
/**
- * Forked invoker implementation, it spawns a subprocess with Maven.
+ * Forked invoker implementation, that spawns a subprocess with Maven from the installation directory.
*/
-public class DefaultForkedMavenInvoker implements ForkedMavenInvoker {
+public class ForkedMavenInvoker implements Invoker {
@SuppressWarnings("MethodLength")
@Override
- public int invoke(ForkedMavenInvokerRequest invokerRequest) throws InvokerException {
+ public int invoke(InvokerRequest invokerRequest) throws InvokerException {
requireNonNull(invokerRequest);
validate(invokerRequest);
@@ -51,7 +51,7 @@ public int invoke(ForkedMavenInvokerRequest invokerRequest) throws InvokerExcept
: invokerRequest.parserRequest().command())
.toString());
- MavenOptions mavenOptions = invokerRequest.options();
+ MavenOptions mavenOptions = (MavenOptions) invokerRequest.options();
if (mavenOptions.userProperties().isPresent()) {
for (Map.Entry entry :
mavenOptions.userProperties().get().entrySet()) {
@@ -216,5 +216,5 @@ public int invoke(ForkedMavenInvokerRequest invokerRequest) throws InvokerExcept
}
}
- protected void validate(ForkedMavenInvokerRequest invokerRequest) throws InvokerException {}
+ protected void validate(InvokerRequest invokerRequest) throws InvokerException {}
}
diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/local/DefaultLocalMavenInvoker.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/local/DefaultLocalMavenInvoker.java
deleted file mode 100644
index 0f68baccdae2..000000000000
--- a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/local/DefaultLocalMavenInvoker.java
+++ /dev/null
@@ -1,54 +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.invoker.mvn.local;
-
-import org.apache.maven.api.cli.mvn.MavenInvokerRequest;
-import org.apache.maven.api.cli.mvn.MavenOptions;
-import org.apache.maven.api.cli.mvn.local.LocalMavenInvoker;
-import org.apache.maven.cling.invoker.ProtoLookup;
-import org.apache.maven.cling.invoker.mvn.DefaultMavenInvoker;
-
-/**
- * Local invoker implementation, when Maven CLI is being run. System uses ClassWorld launcher, and class world
- * instance is passed in via "enhanced" main method. Hence, this class expects fully setup ClassWorld via constructor.
- *
- * @see org.apache.maven.cling.MavenCling
- */
-public class DefaultLocalMavenInvoker
- extends DefaultMavenInvoker<
- MavenOptions, MavenInvokerRequest, DefaultLocalMavenInvoker.LocalContext>
- implements LocalMavenInvoker {
-
- public static class LocalContext
- extends DefaultMavenInvoker.MavenContext<
- MavenOptions, MavenInvokerRequest, DefaultLocalMavenInvoker.LocalContext> {
- protected LocalContext(DefaultLocalMavenInvoker invoker, MavenInvokerRequest invokerRequest) {
- super(invoker, invokerRequest);
- }
- }
-
- public DefaultLocalMavenInvoker(ProtoLookup protoLookup) {
- super(protoLookup);
- }
-
- @Override
- protected LocalContext createContext(MavenInvokerRequest invokerRequest) {
- return new LocalContext(this, invokerRequest);
- }
-}
diff --git a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvn/resident/ResidentMavenInvoker.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/local/LocalMavenInvoker.java
similarity index 57%
rename from api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvn/resident/ResidentMavenInvoker.java
rename to impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/local/LocalMavenInvoker.java
index 5743c7bb2c97..3ac8cc783b46 100644
--- a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvn/resident/ResidentMavenInvoker.java
+++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/local/LocalMavenInvoker.java
@@ -16,24 +16,24 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.maven.api.cli.mvn.resident;
+package org.apache.maven.cling.invoker.mvn.local;
-import org.apache.maven.api.annotations.Experimental;
import org.apache.maven.api.cli.InvokerException;
-import org.apache.maven.api.cli.mvn.MavenInvoker;
-import org.apache.maven.api.cli.mvn.MavenInvokerRequest;
-import org.apache.maven.api.cli.mvn.MavenOptions;
+import org.apache.maven.api.cli.InvokerRequest;
+import org.apache.maven.cling.invoker.ProtoLookup;
+import org.apache.maven.cling.invoker.mvn.MavenContext;
+import org.apache.maven.cling.invoker.mvn.MavenInvoker;
/**
- * Resident Maven invoker, similar to local. Instance is shut down when this instance is closed.
- *
- * @since 4.0.0
+ * Local Maven invoker implementation, that expects all the Maven to be on classpath.
*/
-@Experimental
-public interface ResidentMavenInvoker extends MavenInvoker> {
- /**
- * Closes cleanly the daemon.
- */
+public class LocalMavenInvoker extends MavenInvoker {
+ public LocalMavenInvoker(ProtoLookup protoLookup) {
+ super(protoLookup);
+ }
+
@Override
- void close() throws InvokerException;
+ protected MavenContext createContext(InvokerRequest invokerRequest) throws InvokerException {
+ return new MavenContext(invokerRequest);
+ }
}
diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/package-info.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/package-info.java
new file mode 100644
index 000000000000..60eea96850bb
--- /dev/null
+++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * This package contains the {@code mvn} tool implementation.
+ */
+package org.apache.maven.cling.invoker.mvn;
diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/resident/DefaultResidentMavenInvoker.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/resident/DefaultResidentMavenInvoker.java
deleted file mode 100644
index 77890e9248e1..000000000000
--- a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/resident/DefaultResidentMavenInvoker.java
+++ /dev/null
@@ -1,139 +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.invoker.mvn.resident;
-
-import java.util.ArrayList;
-import java.util.concurrent.ConcurrentHashMap;
-
-import org.apache.maven.api.cli.InvokerException;
-import org.apache.maven.api.cli.mvn.MavenInvokerRequest;
-import org.apache.maven.api.cli.mvn.MavenOptions;
-import org.apache.maven.api.cli.mvn.resident.ResidentMavenInvoker;
-import org.apache.maven.cling.invoker.ProtoLookup;
-import org.apache.maven.cling.invoker.mvn.DefaultMavenInvoker;
-
-/**
- * Local resident invoker implementation, similar to "local" but keeps Maven instance resident. This implies, that
- * things like environment, system properties, extensions etc. are loaded only once. It is caller duty to ensure
- * that subsequent call is right for the resident instance (ie no env change or different extension needed).
- */
-public class DefaultResidentMavenInvoker
- extends DefaultMavenInvoker<
- MavenOptions, MavenInvokerRequest, DefaultResidentMavenInvoker.LocalContext>
- implements ResidentMavenInvoker {
-
- public static class LocalContext
- extends DefaultMavenInvoker.MavenContext<
- MavenOptions, MavenInvokerRequest, DefaultResidentMavenInvoker.LocalContext> {
-
- protected LocalContext(DefaultResidentMavenInvoker invoker, MavenInvokerRequest invokerRequest) {
- super(invoker, invokerRequest);
- }
-
- @Override
- public void close() throws InvokerException {
- // we are resident, we do not shut down here
- }
-
- public void shutDown() throws InvokerException {
- super.close();
- }
-
- public LocalContext copy(MavenInvokerRequest invokerRequest) {
- if (invokerRequest == this.invokerRequest) {
- return this;
- }
- LocalContext shadow = new LocalContext((DefaultResidentMavenInvoker) invoker, invokerRequest);
-
- shadow.logger = logger;
- shadow.loggerFactory = loggerFactory;
- shadow.loggerLevel = loggerLevel;
- shadow.containerCapsule = containerCapsule;
- shadow.lookup = lookup;
- shadow.settingsBuilder = settingsBuilder;
-
- shadow.interactive = interactive;
- shadow.localRepositoryPath = localRepositoryPath;
- shadow.installationSettingsPath = installationSettingsPath;
- shadow.projectSettingsPath = projectSettingsPath;
- shadow.userSettingsPath = userSettingsPath;
- shadow.effectiveSettings = effectiveSettings;
-
- shadow.mavenExecutionRequest = mavenExecutionRequest;
- shadow.eventSpyDispatcher = eventSpyDispatcher;
- shadow.mavenExecutionRequestPopulator = mavenExecutionRequestPopulator;
- shadow.toolchainsBuilder = toolchainsBuilder;
- shadow.modelProcessor = modelProcessor;
- shadow.maven = maven;
-
- return shadow;
- }
- }
-
- private final ConcurrentHashMap residentContext;
-
- public DefaultResidentMavenInvoker(ProtoLookup protoLookup) {
- super(protoLookup);
- this.residentContext = new ConcurrentHashMap<>();
- }
-
- @Override
- public void close() throws InvokerException {
- ArrayList exceptions = new ArrayList<>();
- for (LocalContext context : residentContext.values()) {
- try {
- context.shutDown();
- } catch (InvokerException e) {
- exceptions.add(e);
- }
- }
- if (!exceptions.isEmpty()) {
- InvokerException exception = new InvokerException("Could not cleanly shut down context pool");
- exceptions.forEach(exception::addSuppressed);
- throw exception;
- }
- }
-
- @Override
- protected LocalContext createContext(MavenInvokerRequest invokerRequest) {
- return residentContext
- .computeIfAbsent(getContextId(invokerRequest), k -> new LocalContext(this, invokerRequest))
- .copy(invokerRequest);
- }
-
- protected String getContextId(MavenInvokerRequest invokerRequest) {
- // TODO: in a moment Maven stop pushing user properties to system properties (and maybe something more)
- // and allow multiple instances per JVM, this may become a pool?
- return "resident";
- }
-
- @Override
- protected void container(LocalContext context) throws Exception {
- if (context.containerCapsule == null) {
- super.container(context);
- }
- }
-
- @Override
- protected void lookup(LocalContext context) throws Exception {
- if (context.maven == null) {
- super.lookup(context);
- }
- }
-}
diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/resident/ResidentMavenContext.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/resident/ResidentMavenContext.java
new file mode 100644
index 000000000000..aa1102baa805
--- /dev/null
+++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/resident/ResidentMavenContext.java
@@ -0,0 +1,67 @@
+/*
+ * 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.invoker.mvn.resident;
+
+import org.apache.maven.api.cli.InvokerException;
+import org.apache.maven.api.cli.InvokerRequest;
+import org.apache.maven.cling.invoker.mvn.MavenContext;
+
+public class ResidentMavenContext extends MavenContext {
+
+ protected ResidentMavenContext(InvokerRequest invokerRequest) {
+ super(invokerRequest);
+ }
+
+ @Override
+ public void close() throws InvokerException {
+ // we are resident, we do not shut down here
+ }
+
+ public void shutDown() throws InvokerException {
+ super.close();
+ }
+
+ public ResidentMavenContext copy(InvokerRequest invokerRequest) {
+ if (invokerRequest == this.invokerRequest) {
+ return this;
+ }
+ ResidentMavenContext shadow = new ResidentMavenContext(invokerRequest);
+
+ shadow.logger = logger;
+ shadow.loggerFactory = loggerFactory;
+ shadow.loggerLevel = loggerLevel;
+ shadow.containerCapsule = containerCapsule;
+ shadow.lookup = lookup;
+
+ shadow.interactive = interactive;
+ shadow.localRepositoryPath = localRepositoryPath;
+ shadow.installationSettingsPath = installationSettingsPath;
+ shadow.projectSettingsPath = projectSettingsPath;
+ shadow.userSettingsPath = userSettingsPath;
+ shadow.effectiveSettings = effectiveSettings;
+
+ shadow.mavenExecutionRequest = mavenExecutionRequest;
+ shadow.eventSpyDispatcher = eventSpyDispatcher;
+ shadow.mavenExecutionRequestPopulator = mavenExecutionRequestPopulator;
+ shadow.modelProcessor = modelProcessor;
+ shadow.maven = maven;
+
+ return shadow;
+ }
+}
diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/resident/ResidentMavenInvoker.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/resident/ResidentMavenInvoker.java
new file mode 100644
index 000000000000..95132cdcc939
--- /dev/null
+++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/resident/ResidentMavenInvoker.java
@@ -0,0 +1,86 @@
+/*
+ * 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.invoker.mvn.resident;
+
+import java.util.ArrayList;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.maven.api.cli.InvokerException;
+import org.apache.maven.api.cli.InvokerRequest;
+import org.apache.maven.cling.invoker.ProtoLookup;
+import org.apache.maven.cling.invoker.mvn.MavenInvoker;
+
+/**
+ * Resident invoker implementation, similar to "local", but keeps Maven instance resident. This implies, that
+ * things like environment, system properties, extensions etc. are loaded only once. It is caller duty to ensure
+ * that subsequent call is right for the resident instance (ie no env change or different extension needed).
+ */
+public class ResidentMavenInvoker extends MavenInvoker {
+
+ private final ConcurrentHashMap residentContext;
+
+ public ResidentMavenInvoker(ProtoLookup protoLookup) {
+ super(protoLookup);
+ this.residentContext = new ConcurrentHashMap<>();
+ }
+
+ @Override
+ public void close() throws InvokerException {
+ ArrayList exceptions = new ArrayList<>();
+ for (ResidentMavenContext context : residentContext.values()) {
+ try {
+ context.shutDown();
+ } catch (InvokerException e) {
+ exceptions.add(e);
+ }
+ }
+ if (!exceptions.isEmpty()) {
+ InvokerException exception = new InvokerException("Could not cleanly shut down context pool");
+ exceptions.forEach(exception::addSuppressed);
+ throw exception;
+ }
+ }
+
+ @Override
+ protected ResidentMavenContext createContext(InvokerRequest invokerRequest) {
+ return residentContext
+ .computeIfAbsent(getContextId(invokerRequest), k -> new ResidentMavenContext(invokerRequest))
+ .copy(invokerRequest);
+ }
+
+ protected String getContextId(InvokerRequest invokerRequest) {
+ // TODO: in a moment Maven stop pushing user properties to system properties (and maybe something more)
+ // and allow multiple instances per JVM, this may become a pool?
+ return "resident";
+ }
+
+ @Override
+ protected void container(ResidentMavenContext context) throws Exception {
+ if (context.containerCapsule == null) {
+ super.container(context);
+ }
+ }
+
+ @Override
+ protected void lookup(ResidentMavenContext context) throws Exception {
+ if (context.maven == null) {
+ super.lookup(context);
+ }
+ }
+}
diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/CommonsCliEncryptOptions.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/CommonsCliEncryptOptions.java
index 9ee6ecd1f5be..7ec71cb78778 100644
--- a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/CommonsCliEncryptOptions.java
+++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/CommonsCliEncryptOptions.java
@@ -35,6 +35,9 @@
import static org.apache.maven.cling.invoker.Utils.createInterpolator;
+/**
+ * Implementation of {@link EncryptOptions} (base + mvnenc).
+ */
public class CommonsCliEncryptOptions extends CommonsCliOptions implements EncryptOptions {
public static CommonsCliEncryptOptions parse(String[] args) throws ParseException {
CLIManager cliManager = new CLIManager();
diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/EncryptContext.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/EncryptContext.java
new file mode 100644
index 000000000000..41845f48d2f7
--- /dev/null
+++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/EncryptContext.java
@@ -0,0 +1,54 @@
+/*
+ * 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.invoker.mvnenc;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.maven.api.cli.InvokerRequest;
+import org.apache.maven.cling.invoker.LookupContext;
+import org.jline.consoleui.prompt.ConsolePrompt;
+import org.jline.reader.LineReader;
+import org.jline.utils.AttributedString;
+import org.jline.utils.AttributedStringBuilder;
+import org.jline.utils.AttributedStyle;
+
+@SuppressWarnings("VisibilityModifier")
+public class EncryptContext extends LookupContext {
+ protected EncryptContext(InvokerRequest invokerRequest) {
+ super(invokerRequest);
+ }
+
+ public Map goals;
+
+ public List header;
+ public AttributedStyle style;
+ public LineReader reader;
+ public ConsolePrompt prompt;
+
+ public void addInHeader(String text) {
+ addInHeader(AttributedStyle.DEFAULT, text);
+ }
+
+ public void addInHeader(AttributedStyle style, String text) {
+ AttributedStringBuilder asb = new AttributedStringBuilder();
+ asb.style(style).append(text);
+ header.add(asb.toAttributedString());
+ }
+}
diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/DefaultEncryptInvoker.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/EncryptInvoker.java
similarity index 70%
rename from impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/DefaultEncryptInvoker.java
rename to impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/EncryptInvoker.java
index cbe3cd3ecdc8..87e5425495d6 100644
--- a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/DefaultEncryptInvoker.java
+++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/EncryptInvoker.java
@@ -20,75 +20,41 @@
import java.io.InterruptedIOException;
import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import org.apache.maven.api.cli.mvnenc.EncryptInvoker;
-import org.apache.maven.api.cli.mvnenc.EncryptInvokerRequest;
+import org.apache.maven.api.cli.InvokerRequest;
import org.apache.maven.api.cli.mvnenc.EncryptOptions;
import org.apache.maven.cling.invoker.LookupInvoker;
import org.apache.maven.cling.invoker.ProtoLookup;
import org.apache.maven.cling.utils.CLIReportingUtils;
import org.jline.consoleui.prompt.ConsolePrompt;
-import org.jline.reader.LineReader;
import org.jline.reader.LineReaderBuilder;
import org.jline.reader.UserInterruptException;
import org.jline.terminal.Terminal;
-import org.jline.utils.AttributedString;
-import org.jline.utils.AttributedStringBuilder;
import org.jline.utils.AttributedStyle;
import org.jline.utils.Colors;
import org.jline.utils.OSUtils;
/**
- * Encrypt invoker implementation, when Encrypt CLI is being run. System uses ClassWorld launcher, and class world
- * instance is passed in via "enhanced" main method. Hence, this class expects fully setup ClassWorld via constructor.
+ * mvnenc invoker implementation.
*/
-public class DefaultEncryptInvoker
- extends LookupInvoker
- implements EncryptInvoker {
-
- @SuppressWarnings("VisibilityModifier")
- public static class LocalContext
- extends LookupInvokerContext {
- protected LocalContext(DefaultEncryptInvoker invoker, EncryptInvokerRequest invokerRequest) {
- super(invoker, invokerRequest);
- }
-
- public Map goals;
-
- public List header;
- public AttributedStyle style;
- public LineReader reader;
- public ConsolePrompt prompt;
-
- public void addInHeader(String text) {
- addInHeader(AttributedStyle.DEFAULT, text);
- }
-
- public void addInHeader(AttributedStyle style, String text) {
- AttributedStringBuilder asb = new AttributedStringBuilder();
- asb.style(style).append(text);
- header.add(asb.toAttributedString());
- }
- }
+public class EncryptInvoker extends LookupInvoker {
- public DefaultEncryptInvoker(ProtoLookup protoLookup) {
+ public EncryptInvoker(ProtoLookup protoLookup) {
super(protoLookup);
}
@Override
- protected int execute(LocalContext context) throws Exception {
+ protected int execute(EncryptContext context) throws Exception {
return doExecute(context);
}
@Override
- protected LocalContext createContext(EncryptInvokerRequest invokerRequest) {
- return new LocalContext(this, invokerRequest);
+ protected EncryptContext createContext(InvokerRequest invokerRequest) {
+ return new EncryptContext(invokerRequest);
}
@Override
- protected void lookup(LocalContext context) {
+ protected void lookup(EncryptContext context) {
context.goals = context.lookup.lookupMap(Goal.class);
}
@@ -97,7 +63,7 @@ protected void lookup(LocalContext context) {
public static final int BAD_OPERATION = 2; // bad user input or alike
public static final int CANCELED = 3; // user canceled
- protected int doExecute(LocalContext context) throws Exception {
+ protected int doExecute(EncryptContext context) throws Exception {
try {
if (!context.interactive) {
context.terminal.writer().println("This tool works only in interactive mode!");
@@ -139,12 +105,12 @@ protected int doExecute(LocalContext context) throws Exception {
LineReaderBuilder.builder().terminal(context.terminal).build();
context.prompt = new ConsolePrompt(context.reader, context.terminal, config);
- if (context.invokerRequest.options().goals().isEmpty()
- || context.invokerRequest.options().goals().get().size() != 1) {
+ EncryptOptions options = (EncryptOptions) context.invokerRequest.options();
+ if (options.goals().isEmpty() || options.goals().get().size() != 1) {
return badGoalsErrorMessage("No goal or multiple goals specified, specify only one goal.", context);
}
- String goalName = context.invokerRequest.options().goals().get().get(0);
+ String goalName = options.goals().get().get(0);
Goal goal = context.goals.get(goalName);
if (goal == null) {
@@ -168,7 +134,7 @@ protected int doExecute(LocalContext context) throws Exception {
}
}
- protected int badGoalsErrorMessage(String message, LocalContext context) {
+ protected int badGoalsErrorMessage(String message, EncryptContext context) {
context.terminal.writer().println(message);
context.terminal.writer().println("Supported goals are: " + String.join(", ", context.goals.keySet()));
context.terminal.writer().println("Use -h to display help.");
diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/DefaultEncryptInvokerRequest.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/EncryptInvokerRequest.java
similarity index 85%
rename from impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/DefaultEncryptInvokerRequest.java
rename to impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/EncryptInvokerRequest.java
index de8b52eb6f7c..004e64e4285d 100644
--- a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/DefaultEncryptInvokerRequest.java
+++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/EncryptInvokerRequest.java
@@ -25,20 +25,18 @@
import java.util.Map;
import org.apache.maven.api.annotations.Nonnull;
-import org.apache.maven.api.cli.Options;
import org.apache.maven.api.cli.ParserRequest;
import org.apache.maven.api.cli.extensions.CoreExtension;
-import org.apache.maven.api.cli.mvnenc.EncryptInvokerRequest;
import org.apache.maven.api.cli.mvnenc.EncryptOptions;
import org.apache.maven.cling.invoker.BaseInvokerRequest;
import static java.util.Objects.requireNonNull;
-public class DefaultEncryptInvokerRequest extends BaseInvokerRequest implements EncryptInvokerRequest {
+public class EncryptInvokerRequest extends BaseInvokerRequest {
private final EncryptOptions options;
@SuppressWarnings("ParameterNumber")
- public DefaultEncryptInvokerRequest(
+ public EncryptInvokerRequest(
ParserRequest parserRequest,
Path cwd,
Path installationDirectory,
@@ -51,7 +49,8 @@ public DefaultEncryptInvokerRequest(
OutputStream out,
OutputStream err,
List coreExtensions,
- Options options) {
+ List jvmArguments,
+ EncryptOptions options) {
super(
parserRequest,
cwd,
@@ -64,8 +63,9 @@ public DefaultEncryptInvokerRequest(
in,
out,
err,
- coreExtensions);
- this.options = (EncryptOptions) requireNonNull(options);
+ coreExtensions,
+ jvmArguments);
+ this.options = requireNonNull(options);
}
/**
diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/DefaultEncryptParser.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/EncryptParser.java
similarity index 82%
rename from impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/DefaultEncryptParser.java
rename to impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/EncryptParser.java
index 601563c0dd94..f49fa2e852c0 100644
--- a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/DefaultEncryptParser.java
+++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/EncryptParser.java
@@ -22,16 +22,15 @@
import java.util.List;
import org.apache.commons.cli.ParseException;
+import org.apache.maven.api.cli.Options;
import org.apache.maven.api.cli.ParserException;
-import org.apache.maven.api.cli.mvnenc.EncryptInvokerRequest;
import org.apache.maven.api.cli.mvnenc.EncryptOptions;
-import org.apache.maven.api.cli.mvnenc.EncryptParser;
import org.apache.maven.cling.invoker.BaseParser;
-public class DefaultEncryptParser extends BaseParser implements EncryptParser {
+public class EncryptParser extends BaseParser {
@Override
protected EncryptInvokerRequest getInvokerRequest(LocalContext context) {
- return new DefaultEncryptInvokerRequest(
+ return new EncryptInvokerRequest(
context.parserRequest,
context.cwd,
context.installationDirectory,
@@ -44,11 +43,12 @@ protected EncryptInvokerRequest getInvokerRequest(LocalContext context) {
context.parserRequest.out(),
context.parserRequest.err(),
context.extensions,
- context.options);
+ getJvmArguments(context.rootDirectory),
+ (EncryptOptions) context.options);
}
@Override
- protected List parseCliOptions(LocalContext context) throws ParserException {
+ protected List parseCliOptions(LocalContext context) throws ParserException {
return Collections.singletonList(parseEncryptCliOptions(context.parserRequest.args()));
}
@@ -61,7 +61,7 @@ protected CommonsCliEncryptOptions parseEncryptCliOptions(List args) thr
}
@Override
- protected EncryptOptions assembleOptions(List parsedOptions) {
+ protected Options assembleOptions(List parsedOptions) {
// nothing to assemble, we deal with CLI only
return parsedOptions.get(0);
}
diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/Goal.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/Goal.java
index d493b289e36c..43d2dc8b29ac 100644
--- a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/Goal.java
+++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/Goal.java
@@ -22,5 +22,5 @@
* The mvnenc tool goal.
*/
public interface Goal {
- int execute(DefaultEncryptInvoker.LocalContext context) throws Exception;
+ int execute(EncryptContext context) throws Exception;
}
diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/goals/ConfiguredGoalSupport.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/goals/ConfiguredGoalSupport.java
index efd4792f5c95..fc9a51ad846d 100644
--- a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/goals/ConfiguredGoalSupport.java
+++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/goals/ConfiguredGoalSupport.java
@@ -23,10 +23,10 @@
import java.util.function.Consumer;
import org.apache.maven.api.services.MessageBuilderFactory;
-import org.apache.maven.cling.invoker.mvnenc.DefaultEncryptInvoker;
+import org.apache.maven.cling.invoker.mvnenc.EncryptContext;
import org.codehaus.plexus.components.secdispatcher.SecDispatcher;
-import static org.apache.maven.cling.invoker.mvnenc.DefaultEncryptInvoker.ERROR;
+import static org.apache.maven.cling.invoker.mvnenc.EncryptInvoker.ERROR;
/**
* The support class for goal implementations that requires valid/workable config.
@@ -37,7 +37,7 @@ protected ConfiguredGoalSupport(MessageBuilderFactory messageBuilderFactory, Sec
}
@Override
- public int execute(DefaultEncryptInvoker.LocalContext context) throws Exception {
+ public int execute(EncryptContext context) throws Exception {
if (!validateConfiguration(context)) {
context.terminal
.writer()
@@ -50,7 +50,7 @@ public int execute(DefaultEncryptInvoker.LocalContext context) throws Exception
return doExecute(context);
}
- protected boolean validateConfiguration(DefaultEncryptInvoker.LocalContext context) {
+ protected boolean validateConfiguration(EncryptContext context) {
SecDispatcher.ValidationResponse response = secDispatcher.validateConfiguration();
if (!response.isValid() || context.invokerRequest.options().verbose().orElse(false)) {
dumpResponse(context, "", response);
@@ -58,8 +58,7 @@ protected boolean validateConfiguration(DefaultEncryptInvoker.LocalContext conte
return response.isValid();
}
- protected void dumpResponse(
- DefaultEncryptInvoker.LocalContext context, String indent, SecDispatcher.ValidationResponse response) {
+ protected void dumpResponse(EncryptContext context, String indent, SecDispatcher.ValidationResponse response) {
context.terminal
.writer()
.println(messageBuilderFactory
@@ -100,5 +99,5 @@ protected void dumpResponse(
}
}
- protected abstract int doExecute(DefaultEncryptInvoker.LocalContext context) throws Exception;
+ protected abstract int doExecute(EncryptContext context) throws Exception;
}
diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/goals/Decrypt.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/goals/Decrypt.java
index 43be1949e5a7..e58ac80bcd9b 100644
--- a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/goals/Decrypt.java
+++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/goals/Decrypt.java
@@ -23,11 +23,11 @@
import javax.inject.Singleton;
import org.apache.maven.api.services.MessageBuilderFactory;
-import org.apache.maven.cling.invoker.mvnenc.DefaultEncryptInvoker;
+import org.apache.maven.cling.invoker.mvnenc.EncryptContext;
import org.codehaus.plexus.components.secdispatcher.SecDispatcher;
-import static org.apache.maven.cling.invoker.mvnenc.DefaultEncryptInvoker.BAD_OPERATION;
-import static org.apache.maven.cling.invoker.mvnenc.DefaultEncryptInvoker.OK;
+import static org.apache.maven.cling.invoker.mvnenc.EncryptInvoker.BAD_OPERATION;
+import static org.apache.maven.cling.invoker.mvnenc.EncryptInvoker.OK;
/**
* The "decrypt" goal.
@@ -41,7 +41,7 @@ public Decrypt(MessageBuilderFactory messageBuilderFactory, SecDispatcher secDis
}
@Override
- protected int doExecute(DefaultEncryptInvoker.LocalContext context) throws Exception {
+ protected int doExecute(EncryptContext context) throws Exception {
String encrypted = context.reader.readLine("Enter the password to decrypt: ");
if (secDispatcher.isAnyEncryptedString(encrypted)) {
context.terminal.writer().println(secDispatcher.decrypt(encrypted));
diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/goals/Diag.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/goals/Diag.java
index 1f85bbed48b5..700ed47bc3eb 100644
--- a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/goals/Diag.java
+++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/goals/Diag.java
@@ -23,10 +23,10 @@
import javax.inject.Singleton;
import org.apache.maven.api.services.MessageBuilderFactory;
-import org.apache.maven.cling.invoker.mvnenc.DefaultEncryptInvoker;
+import org.apache.maven.cling.invoker.mvnenc.EncryptContext;
import org.codehaus.plexus.components.secdispatcher.SecDispatcher;
-import static org.apache.maven.cling.invoker.mvnenc.DefaultEncryptInvoker.OK;
+import static org.apache.maven.cling.invoker.mvnenc.EncryptInvoker.OK;
/**
* The "diag" goal.
@@ -40,7 +40,7 @@ public Diag(MessageBuilderFactory messageBuilderFactory, SecDispatcher secDispat
}
@Override
- protected int doExecute(DefaultEncryptInvoker.LocalContext context) {
+ protected int doExecute(EncryptContext context) {
dumpResponse(context, "", secDispatcher.validateConfiguration());
return OK;
}
diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/goals/Encrypt.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/goals/Encrypt.java
index a3fce98d61d0..bbe77f54a002 100644
--- a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/goals/Encrypt.java
+++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/goals/Encrypt.java
@@ -23,10 +23,10 @@
import javax.inject.Singleton;
import org.apache.maven.api.services.MessageBuilderFactory;
-import org.apache.maven.cling.invoker.mvnenc.DefaultEncryptInvoker;
+import org.apache.maven.cling.invoker.mvnenc.EncryptContext;
import org.codehaus.plexus.components.secdispatcher.SecDispatcher;
-import static org.apache.maven.cling.invoker.mvnenc.DefaultEncryptInvoker.OK;
+import static org.apache.maven.cling.invoker.mvnenc.EncryptInvoker.OK;
/**
* The "encrypt" goal.
@@ -40,7 +40,7 @@ public Encrypt(MessageBuilderFactory messageBuilderFactory, SecDispatcher secDis
}
@Override
- protected int doExecute(DefaultEncryptInvoker.LocalContext context) throws Exception {
+ protected int doExecute(EncryptContext context) throws Exception {
String cleartext = context.reader.readLine("Enter the password to encrypt: ", '*');
context.terminal.writer().println(secDispatcher.encrypt(cleartext, null));
return OK;
diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/goals/Init.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/goals/Init.java
index 0a98d6c0b194..3c78188376f8 100644
--- a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/goals/Init.java
+++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/goals/Init.java
@@ -26,8 +26,9 @@
import java.util.Map;
import java.util.Objects;
+import org.apache.maven.api.cli.mvnenc.EncryptOptions;
import org.apache.maven.api.services.MessageBuilderFactory;
-import org.apache.maven.cling.invoker.mvnenc.DefaultEncryptInvoker;
+import org.apache.maven.cling.invoker.mvnenc.EncryptContext;
import org.codehaus.plexus.components.secdispatcher.DispatcherMeta;
import org.codehaus.plexus.components.secdispatcher.SecDispatcher;
import org.codehaus.plexus.components.secdispatcher.model.Config;
@@ -45,8 +46,8 @@
import org.jline.reader.ParsedLine;
import org.jline.utils.Colors;
-import static org.apache.maven.cling.invoker.mvnenc.DefaultEncryptInvoker.BAD_OPERATION;
-import static org.apache.maven.cling.invoker.mvnenc.DefaultEncryptInvoker.OK;
+import static org.apache.maven.cling.invoker.mvnenc.EncryptInvoker.BAD_OPERATION;
+import static org.apache.maven.cling.invoker.mvnenc.EncryptInvoker.OK;
/**
* The "init" goal.
@@ -62,13 +63,15 @@ public Init(MessageBuilderFactory messageBuilderFactory, SecDispatcher secDispat
}
@Override
- public int execute(DefaultEncryptInvoker.LocalContext context) throws Exception {
+ public int execute(EncryptContext context) throws Exception {
context.addInHeader(context.style.italic().bold().foreground(Colors.rgbColor("yellow")), "goal: init");
context.addInHeader("");
ConsolePrompt prompt = context.prompt;
- boolean force = context.invokerRequest.options().force().orElse(false);
- boolean yes = context.invokerRequest.options().yes().orElse(false);
+
+ EncryptOptions options = (EncryptOptions) context.invokerRequest.options();
+ boolean force = options.force().orElse(false);
+ boolean yes = options.yes().orElse(false);
if (configExists() && !force) {
context.terminal
@@ -238,8 +241,7 @@ protected PromptBuilder dispatcherPrompt(PromptBuilder promptBuilder) {
}
private PromptBuilder configureDispatcher(
- DefaultEncryptInvoker.LocalContext context, DispatcherMeta dispatcherMeta, PromptBuilder promptBuilder)
- throws Exception {
+ EncryptContext context, DispatcherMeta dispatcherMeta, PromptBuilder promptBuilder) throws Exception {
context.addInHeader(
context.style.italic().bold().foreground(Colors.rgbColor("yellow")),
"Configure " + dispatcherMeta.displayName());
diff --git a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvn/forked/ForkedMavenInvoker.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/package-info.java
similarity index 73%
rename from api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvn/forked/ForkedMavenInvoker.java
rename to impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/package-info.java
index ee75b2400b1d..69376e787296 100644
--- a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/mvn/forked/ForkedMavenInvoker.java
+++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/package-info.java
@@ -16,15 +16,8 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.maven.api.cli.mvn.forked;
-
-import org.apache.maven.api.annotations.Experimental;
-import org.apache.maven.api.cli.mvn.MavenInvoker;
/**
- * Forked Maven invoker.
- *
- * @since 4.0.0
+ * This package contains the {@code mvnenc} tool implementation.
*/
-@Experimental
-public interface ForkedMavenInvoker extends MavenInvoker {}
+package org.apache.maven.cling.invoker.mvnenc;
diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/package-info.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/package-info.java
new file mode 100644
index 000000000000..b169eba8dbf4
--- /dev/null
+++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * This package contain support (mostly abstract) classes, that implement "base" of CLIng.
+ * In packages below you find actual implementations.
+ */
+package org.apache.maven.cling.invoker;
diff --git a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenExecutorTestSupport.java b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenExecutorTestSupport.java
new file mode 100644
index 000000000000..c3c487dcc1c7
--- /dev/null
+++ b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenExecutorTestSupport.java
@@ -0,0 +1,64 @@
+/*
+ * 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.invoker.mvn;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.maven.api.cli.Executor;
+import org.apache.maven.api.cli.Parser;
+import org.apache.maven.api.cli.ParserRequest;
+import org.apache.maven.cling.invoker.ProtoLogger;
+import org.apache.maven.jline.JLineMessageBuilderFactory;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public abstract class MavenExecutorTestSupport {
+
+ protected void execute(Path cwd, Collection goals) throws Exception {
+ Files.createDirectory(cwd.resolve(".mvn"));
+ Path pom = cwd.resolve("pom.xml").toAbsolutePath();
+ Files.writeString(pom, MavenTestSupport.POM_STRING);
+ Path appJava = cwd.resolve("src/main/java/org/apache/maven/samples/sample/App.java");
+ Files.createDirectories(appJava.getParent());
+ Files.writeString(appJava, MavenTestSupport.APP_JAVA_STRING);
+
+ Parser parser = createParser();
+ try (Executor invoker = createExecutor()) {
+ for (String goal : goals) {
+ Path logFile = cwd.resolve(goal + "-build.log").toAbsolutePath();
+ int exitCode = invoker.execute(parser.parseExecution(ParserRequest.mvn(
+ List.of("-l", logFile.toString(), goal),
+ new ProtoLogger(),
+ new JLineMessageBuilderFactory())
+ .cwd(cwd)
+ .build()));
+ String log = Files.readString(logFile);
+ System.out.println(log);
+ assertEquals(0, exitCode, log);
+ }
+ }
+ }
+
+ protected abstract Executor createExecutor();
+
+ protected abstract Parser createParser();
+}
diff --git a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenInvokerTestSupport.java b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenInvokerTestSupport.java
index e2dd847053a8..d3dcf3fc9a20 100644
--- a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenInvokerTestSupport.java
+++ b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenInvokerTestSupport.java
@@ -27,15 +27,13 @@
import org.apache.maven.api.cli.Invoker;
import org.apache.maven.api.cli.Parser;
import org.apache.maven.api.cli.ParserRequest;
-import org.apache.maven.api.cli.mvn.MavenInvokerRequest;
-import org.apache.maven.api.cli.mvn.MavenOptions;
import org.apache.maven.cling.invoker.ProtoLogger;
import org.apache.maven.jline.JLineMessageBuilderFactory;
import org.junit.jupiter.api.Assumptions;
import static org.junit.jupiter.api.Assertions.assertEquals;
-public abstract class MavenInvokerTestSupport> {
+public abstract class MavenInvokerTestSupport {
protected void invoke(Path cwd, Collection goals) throws Exception {
// works only in recent Maven4
@@ -46,75 +44,30 @@ protected void invoke(Path cwd, Collection goals) throws Exception {
"${maven.home}/conf/maven.properties must be a file");
Files.createDirectory(cwd.resolve(".mvn"));
-
- String pomString =
- """
-
-
-
- 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
-
-
-
-
- """;
Path pom = cwd.resolve("pom.xml").toAbsolutePath();
- Files.writeString(pom, pomString);
-
- String appJavaString =
- """
- package org.apache.maven.samples.sample;
-
- public class App {
- public static void main(String... args) {
- System.out.println("Hello World!");
- }
- }
- """;
+ Files.writeString(pom, MavenTestSupport.POM_STRING);
Path appJava = cwd.resolve("src/main/java/org/apache/maven/samples/sample/App.java");
Files.createDirectories(appJava.getParent());
- Files.writeString(appJava, appJavaString);
+ Files.writeString(appJava, MavenTestSupport.APP_JAVA_STRING);
- Parser parser = createParser();
- try (Invoker invoker = createInvoker()) {
+ Parser parser = createParser();
+ try (Invoker invoker = createInvoker()) {
for (String goal : goals) {
Path logFile = cwd.resolve(goal + "-build.log").toAbsolutePath();
- int exitCode = invoker.invoke(parser.parse(ParserRequest.mvn(
+ int exitCode = invoker.invoke(parser.parseInvocation(ParserRequest.mvn(
List.of("-l", logFile.toString(), goal),
new ProtoLogger(),
new JLineMessageBuilderFactory())
.cwd(cwd)
.build()));
String log = Files.readString(logFile);
+ System.out.println(log);
assertEquals(0, exitCode, log);
}
}
}
- protected abstract Invoker createInvoker();
+ protected abstract Invoker createInvoker();
- protected abstract Parser createParser();
+ protected abstract Parser createParser();
}
diff --git a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenTestSupport.java b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenTestSupport.java
new file mode 100644
index 000000000000..9c1ebeff36d5
--- /dev/null
+++ b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenTestSupport.java
@@ -0,0 +1,69 @@
+/*
+ * 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.invoker.mvn;
+
+public final class MavenTestSupport {
+ private MavenTestSupport() {}
+
+ 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!");
+ }
+ }
+ """;
+}
diff --git a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/embedded/EmbeddedMavenExecutorTest.java b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/embedded/EmbeddedMavenExecutorTest.java
new file mode 100644
index 000000000000..7199d43fdd31
--- /dev/null
+++ b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/embedded/EmbeddedMavenExecutorTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.invoker.mvn.embedded;
+
+import java.nio.file.Path;
+import java.util.List;
+
+import org.apache.maven.api.cli.Executor;
+import org.apache.maven.api.cli.Parser;
+import org.apache.maven.cling.invoker.mvn.MavenExecutorTestSupport;
+import org.apache.maven.cling.invoker.mvn.MavenParser;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.CleanupMode;
+import org.junit.jupiter.api.io.TempDir;
+
+/**
+ * Forked UT: it cannot use jimFS as it runs in child process.
+ */
+@Disabled(
+ "The tests reuse properties from the JVM being launched, thus may lead to failures depending on which options are used")
+public class EmbeddedMavenExecutorTest extends MavenExecutorTestSupport {
+
+ @Override
+ protected Executor createExecutor() {
+ return new EmbeddedMavenExecutor();
+ }
+
+ @Override
+ protected Parser createParser() {
+ return new MavenParser();
+ }
+
+ @Test
+ void defaultFs(@TempDir(cleanup = CleanupMode.ON_SUCCESS) Path tempDir) throws Exception {
+ System.setProperty("maven.home", "/home/cstamas/Tools/maven/apache-maven-4.0.0-beta-6-SNAPSHOT");
+ execute(tempDir, List.of("verify"));
+ }
+
+ @Test
+ void defaultFs3x(@TempDir(cleanup = CleanupMode.ON_SUCCESS) Path tempDir) throws Exception {
+ System.setProperty("maven.home", "/home/cstamas/.sdkman/candidates/maven/3.9.9");
+ execute(tempDir, List.of("verify"));
+ }
+}
diff --git a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/forked/DefaultForkedMavenInvokerTest.java b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/forked/ForkedMavenInvokerTest.java
similarity index 76%
rename from impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/forked/DefaultForkedMavenInvokerTest.java
rename to impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/forked/ForkedMavenInvokerTest.java
index f03c161d4c9e..01ddfe3267c5 100644
--- a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/forked/DefaultForkedMavenInvokerTest.java
+++ b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/forked/ForkedMavenInvokerTest.java
@@ -23,9 +23,9 @@
import org.apache.maven.api.cli.Invoker;
import org.apache.maven.api.cli.Parser;
-import org.apache.maven.api.cli.mvn.MavenOptions;
-import org.apache.maven.api.cli.mvn.forked.ForkedMavenInvokerRequest;
import org.apache.maven.cling.invoker.mvn.MavenInvokerTestSupport;
+import org.apache.maven.cling.invoker.mvn.MavenParser;
+import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.CleanupMode;
import org.junit.jupiter.api.io.TempDir;
@@ -33,16 +33,18 @@
/**
* Forked UT: it cannot use jimFS as it runs in child process.
*/
-public class DefaultForkedMavenInvokerTest extends MavenInvokerTestSupport {
+@Disabled(
+ "The tests reuse properties from the JVM being launched, thus may lead to failures depending on which options are used")
+public class ForkedMavenInvokerTest extends MavenInvokerTestSupport {
@Override
- protected Invoker createInvoker() {
- return new DefaultForkedMavenInvoker();
+ protected Invoker createInvoker() {
+ return new ForkedMavenInvoker();
}
@Override
- protected Parser createParser() {
- return new DefaultForkedMavenParser();
+ protected Parser createParser() {
+ return new MavenParser();
}
@Test
diff --git a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/local/DefaultLocalMavenInvokerTest.java b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/local/DefaultLocalMavenInvokerTest.java
index 904ac1cff01d..d4389788fab9 100644
--- a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/local/DefaultLocalMavenInvokerTest.java
+++ b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/local/DefaultLocalMavenInvokerTest.java
@@ -26,11 +26,9 @@
import com.google.common.jimfs.Jimfs;
import org.apache.maven.api.cli.Invoker;
import org.apache.maven.api.cli.Parser;
-import org.apache.maven.api.cli.mvn.MavenInvokerRequest;
-import org.apache.maven.api.cli.mvn.MavenOptions;
import org.apache.maven.cling.invoker.ProtoLookup;
-import org.apache.maven.cling.invoker.mvn.DefaultMavenParser;
import org.apache.maven.cling.invoker.mvn.MavenInvokerTestSupport;
+import org.apache.maven.cling.invoker.mvn.MavenParser;
import org.codehaus.plexus.classworlds.ClassWorld;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
@@ -42,18 +40,17 @@
*/
@Disabled(
"The tests reuse properties from the JVM being launched, thus may lead to failures depending on which options are used")
-public class DefaultLocalMavenInvokerTest
- extends MavenInvokerTestSupport> {
+public class DefaultLocalMavenInvokerTest extends MavenInvokerTestSupport {
@Override
- protected Invoker> createInvoker() {
- return new DefaultLocalMavenInvoker(ProtoLookup.builder()
+ protected Invoker createInvoker() {
+ return new LocalMavenInvoker(ProtoLookup.builder()
.addMapping(ClassWorld.class, new ClassWorld("plexus.core", ClassLoader.getSystemClassLoader()))
.build());
}
@Override
- protected Parser> createParser() {
- return new DefaultMavenParser();
+ protected Parser createParser() {
+ return new MavenParser();
}
@Test
diff --git a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/resident/DefaultResidentMavenInvokerTest.java b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/resident/DefaultResidentMavenInvokerTest.java
index 73485b9797ce..de65db950313 100644
--- a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/resident/DefaultResidentMavenInvokerTest.java
+++ b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/resident/DefaultResidentMavenInvokerTest.java
@@ -26,11 +26,9 @@
import com.google.common.jimfs.Jimfs;
import org.apache.maven.api.cli.Invoker;
import org.apache.maven.api.cli.Parser;
-import org.apache.maven.api.cli.mvn.MavenInvokerRequest;
-import org.apache.maven.api.cli.mvn.MavenOptions;
import org.apache.maven.cling.invoker.ProtoLookup;
-import org.apache.maven.cling.invoker.mvn.DefaultMavenParser;
import org.apache.maven.cling.invoker.mvn.MavenInvokerTestSupport;
+import org.apache.maven.cling.invoker.mvn.MavenParser;
import org.codehaus.plexus.classworlds.ClassWorld;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
@@ -42,19 +40,18 @@
*/
@Disabled(
"The tests reuse properties from the JVM being launched, thus may lead to failures depending on which options are used")
-public class DefaultResidentMavenInvokerTest
- extends MavenInvokerTestSupport> {
+public class DefaultResidentMavenInvokerTest extends MavenInvokerTestSupport {
@Override
- protected Invoker> createInvoker() {
- return new DefaultResidentMavenInvoker(ProtoLookup.builder()
+ protected Invoker createInvoker() {
+ return new ResidentMavenInvoker(ProtoLookup.builder()
.addMapping(ClassWorld.class, new ClassWorld("plexus.core", ClassLoader.getSystemClassLoader()))
.build());
}
@Override
- protected Parser> createParser() {
- return new DefaultMavenParser();
+ protected Parser createParser() {
+ return new MavenParser();
}
@Test