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