[MNG-8375] CLIng diet (#1897)

First part was to "reverse" MavenCli into CLIng, that also became a huge and complex beast. Now, sanitization comes, tearing down unneeded stuff. CLIng should be simple and straightforward.

Now the **invoker** fully parses args, creates Maven instance (ie. local, using Maven components on classpath) and invokes Maven. The new **executor** in turn does NOT fully parses args and is logical equivalent of maven-invoker.

Changes:
* radically simplify CLIng existing classes (and especially API - the fact we have "local", "resident" etc invoker is actually implementation detail).
* introduce "executor", a "lower level" tool that does not parse args (and is logical equivalent of maven-invoker) and support Maven 4.x and Maven 3.x.

---

https://issues.apache.org/jira/browse/MNG-8375
This commit is contained in:
Tamas Cservenak 2024-11-13 15:14:56 +01:00 committed by GitHub
parent c7effeb15c
commit a67d2746af
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
67 changed files with 1586 additions and 1371 deletions

1
.mvn/maven.config Normal file
View File

@ -0,0 +1 @@
-Daether.transport.jdk.httpVersion=HTTP_1_1

View File

@ -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.
*
* <p>The default implementation does nothing. Subclasses should override this method
* if they need to perform cleanup operations.</p>
*
* @throws ExecutorException if an error occurs while closing the {@link Executor}
*/
@Override
default void close() throws ExecutorException {}
}

View File

@ -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);
}
}

View File

@ -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<List<String>> jvmArguments();
}

View File

@ -24,18 +24,16 @@ import org.apache.maven.api.annotations.Nonnull;
/**
* 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.
*
* <p>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.</p>
*
* @param <R> The specific type of {@link InvokerRequest} this {@code Invoker} can handle, extending {@link InvokerRequest}
*
* @since 4.0.0
*/
@Experimental
public interface Invoker<R extends InvokerRequest<? extends Options>> 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<R extends InvokerRequest<? extends Options>> 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.

View File

@ -49,4 +49,21 @@ public class InvokerException extends MavenException {
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;
}
}
}

View File

@ -33,22 +33,14 @@ import org.apache.maven.api.services.Lookup;
import org.apache.maven.api.services.MessageBuilderFactory;
/**
* Represents a Maven execution request, encapsulating all necessary information
* for invoking a Maven build or command.
*
* @param <O> 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<O extends Options> {
/**
* 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 @@ public interface InvokerRequest<O extends Options> {
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 @@ public interface InvokerRequest<O extends Options> {
* @return the options object
*/
@Nonnull
O options();
Options options();
}

View File

@ -22,47 +22,35 @@ import java.io.IOException;
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 <R> 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<R extends InvokerRequest<? extends Options>> {
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)}.
*
* @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
* @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());
}
/**
* 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 ExecutorRequest}.
* This method does not interpret tool arguments.
*
* @param parserRequest the request containing all necessary information for parsing
* @return the parsed InvokerRequest
* @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
R parse(@Nonnull ParserRequest parserRequest) throws ParserException, IOException;
ExecutorRequest parseExecution(@Nonnull ParserRequest parserRequest) throws ParserException, IOException;
/**
* 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 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
InvokerRequest parseInvocation(@Nonnull ParserRequest parserRequest) throws ParserException, IOException;
}

View File

@ -46,12 +46,6 @@ import static java.util.Objects.requireNonNull;
@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 @@ public interface ParserRequest {
@Nonnull
static Builder mvn(
@Nonnull List<String> 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 @@ public interface ParserRequest {
@Nonnull
static Builder mvnenc(
@Nonnull List<String> 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);
}
/**

View File

@ -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<MavenInvokerRequest<MavenOptions>> {}
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";
}

View File

@ -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 <R> The specific type of {@link MavenInvokerRequest} this invoker can handle
*
* @since 4.0.0
*/
@Experimental
public interface MavenInvoker<R extends MavenInvokerRequest<? extends MavenOptions>> extends Invoker<R> {
/**
* 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;
}

View File

@ -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.
*
* <p>A {@link MavenInvokerRequest} encapsulates all the necessary information needed to perform
* a Maven build, including any Maven-specific options defined in {@link MavenOptions}.</p>
*
* @param <O> The specific Options type this request carries
*
* @since 4.0.0
*/
@Experimental
public interface MavenInvokerRequest<O extends MavenOptions> extends InvokerRequest<O> {
// 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.
}

View File

@ -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 <R> The specific type of MavenInvokerRequest this parser produces
*
* @since 4.0.0
*/
@Experimental
public interface MavenParser<R extends MavenInvokerRequest<? extends MavenOptions>> extends Parser<R> {
/**
* 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;
}

View File

@ -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<MavenOptions> {
/**
* 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<List<String>> jvmArguments();
}

View File

@ -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<ForkedMavenInvokerRequest> {
/**
* 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;
}

View File

@ -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.
*
* <p>The EncryptInvoker is designed to handle encryption tasks within the Maven ecosystem,
* such as encrypting passwords or other sensitive information in Maven settings files.</p>
*
* @since 4.0.0
*/
@Experimental
public interface EncryptInvoker extends Invoker<EncryptInvokerRequest> {
/**
* 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;
}

View File

@ -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.
*
* <p>An EncryptInvokerRequest encapsulates all the necessary information needed to perform
* an encryption operation, including any encryption-specific options defined in EncryptOptions.</p>
*
* @since 4.0.0
*/
@Experimental
public interface EncryptInvokerRequest extends InvokerRequest<EncryptOptions> {
// 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.
}

View File

@ -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<EncryptInvokerRequest> {
/**
* 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;
}

View File

@ -23,7 +23,6 @@ import java.io.IOException;
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 @@ import static java.util.Objects.requireNonNull;
/**
* The CLI "new-gen".
*
* @param <O> the options type
* @param <R> the request type
*/
public abstract class ClingSupport<O extends Options, R extends InvokerRequest<O>> {
public abstract class ClingSupport {
static final String CORE_CLASS_REALM_ID = "plexus.core";
protected final ClassWorld classWorld;
@ -64,7 +60,7 @@ public abstract class ClingSupport<O extends Options, R extends InvokerRequest<O
* The main entry point.
*/
public int run(String[] args) throws IOException {
try (Invoker<R> invoker = createInvoker()) {
try (Invoker invoker = createInvoker()) {
return invoker.invoke(parseArguments(args));
} catch (ParserException e) {
System.err.println(e.getMessage());
@ -78,7 +74,7 @@ public abstract class ClingSupport<O extends Options, R extends InvokerRequest<O
}
}
protected abstract Invoker<R> createInvoker();
protected abstract Invoker createInvoker();
protected abstract R parseArguments(String[] args) throws ParserException, IOException;
protected abstract InvokerRequest parseArguments(String[] args) throws ParserException, IOException;
}

View File

@ -21,20 +21,20 @@ package org.apache.maven.cling;
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<MavenOptions, MavenInvokerRequest<MavenOptions>> {
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 class MavenCling extends ClingSupport<MavenOptions, MavenInvokerRequest<M
}
@Override
protected Invoker<MavenInvokerRequest<MavenOptions>> createInvoker() {
return new DefaultLocalMavenInvoker(
protected Invoker createInvoker() {
return new LocalMavenInvoker(
ProtoLookup.builder().addMapping(ClassWorld.class, classWorld).build());
}
@Override
protected MavenInvokerRequest<MavenOptions> 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());
}
}

View File

@ -21,21 +21,20 @@ package org.apache.maven.cling;
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<EncryptOptions, EncryptInvokerRequest> {
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 class MavenEncCling extends ClingSupport<EncryptOptions, EncryptInvokerRe
}
@Override
protected Invoker<EncryptInvokerRequest> 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());
}
}

View File

@ -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<String> jvmArguments;
@SuppressWarnings("ParameterNumber")
public BaseExecutorRequest(
@Nonnull ParserRequest parserRequest,
@Nonnull Path cwd,
@Nonnull Path installationDirectory,
@Nonnull Path userHomeDirectory,
@Nullable List<String> 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<List<String>> jvmArguments() {
return Optional.ofNullable(jvmArguments);
}
}

View File

@ -28,26 +28,20 @@ import java.util.Optional;
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<T extends Options> implements InvokerRequest<T> {
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<String, String> userProperties;
private final Map<String, String> systemProperties;
private final Path topDirectory;
private final Path rootDirectory;
private final List<CoreExtension> coreExtensions;
private final InputStream in;
private final OutputStream out;
private final OutputStream err;
private final List<CoreExtension> coreExtensions;
@SuppressWarnings("ParameterNumber")
public BaseInvokerRequest(
@ -62,40 +56,18 @@ public abstract class BaseInvokerRequest<T extends Options> implements InvokerRe
@Nullable InputStream in,
@Nullable OutputStream out,
@Nullable OutputStream err,
@Nullable List<CoreExtension> coreExtensions) {
this.parserRequest = requireNonNull(parserRequest);
this.cwd = requireNonNull(cwd);
this.installationDirectory = requireNonNull(installationDirectory);
this.userHomeDirectory = requireNonNull(userHomeDirectory);
@Nullable List<CoreExtension> coreExtensions,
@Nullable List<String> 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

View File

@ -33,9 +33,11 @@ import java.util.List;
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.prefix;
import static org.apache.maven.cling.invoker.Utils.stripLeadingAndTrailingQuotes;
import static org.apache.maven.cling.invoker.Utils.toMap;
public abstract class BaseParser<O extends Options, R extends InvokerRequest<O>> implements Parser<R> {
public abstract class BaseParser implements Parser {
@SuppressWarnings("VisibilityModifier")
public static class LocalContext {
@ -93,7 +95,26 @@ public abstract class BaseParser<O extends Options, R extends InvokerRequest<O>>
}
@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 abstract class BaseParser<O extends Options, R extends InvokerRequest<O>>
context.rootDirectory = getRootDirectory(context);
// options
List<O> parsedOptions = parseCliOptions(context);
List<Options> parsedOptions = parseCliOptions(context);
// warn about deprecated options
PrintWriter printWriter = new PrintWriter(parserRequest.out() != null ? parserRequest.out() : System.out, true);
@ -131,7 +152,7 @@ public abstract class BaseParser<O extends Options, R extends InvokerRequest<O>>
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 @@ public abstract class BaseParser<O extends Options, R extends InvokerRequest<O>>
return toMap(userProperties);
}
protected abstract List<O> parseCliOptions(LocalContext context) throws ParserException, IOException;
protected abstract List<Options> parseCliOptions(LocalContext context) throws ParserException, IOException;
protected abstract O assembleOptions(List<O> parsedOptions);
protected abstract Options assembleOptions(List<Options> parsedOptions);
protected List<CoreExtension> readCoreExtensionsDescriptor(LocalContext context)
throws ParserException, IOException {
@ -304,4 +325,21 @@ public abstract class BaseParser<O extends Options, R extends InvokerRequest<O>>
throw new ParserException("Failed to parse extensions file: " + extensionsFile, e);
}
}
protected List<String> 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;
}
}

View File

@ -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<ClassLoader> currentThreadClassLoader();
/**
* Performs a clean shutdown of backing container.
*/

View File

@ -20,21 +20,16 @@ package org.apache.maven.cling.invoker;
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 <O> the options type
* @param <R> the invoker request type
* @param <C> the invoker context type
* @param <C> The context type.
*/
public interface ContainerCapsuleFactory<
O extends Options, R extends InvokerRequest<O>, C extends LookupInvoker.LookupInvokerContext<O, R, C>> {
public interface ContainerCapsuleFactory<C extends LookupContext> {
/**
* Creates container capsule.
*/
@Nonnull
ContainerCapsule createContainerCapsule(C context) throws InvokerException;
ContainerCapsule createContainerCapsule(LookupInvoker<C> invoker, C context) throws InvokerException;
}

View File

@ -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<String, Path> cwdResolver;
public final Function<String, Path> installationResolver;
public final Function<String, Path> 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<String, String> user = new HashMap<>(invokerRequest.userProperties());
user.put("session.rootDirectory", invokerRequest.rootDirectory().toString());
user.put("session.topDirectory", invokerRequest.topDirectory().toString());
Map<String, String> 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<String> 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<AutoCloseable> closeables = new ArrayList<>();
@Override
public void close() throws InvokerException {
List<Exception> causes = null;
List<AutoCloseable> 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;
}
}
}

View File

@ -24,8 +24,6 @@ import java.io.PrintWriter;
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.Consumer;
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.Logger;
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.ArtifactRepositoryPolicy;
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.Terminal;
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.toMavenExecutionRequestLoggin
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 <O> the options type
* @param <R> the request type
* @param <C> the context type
* @param <C> The context type.
*/
public abstract class LookupInvoker<
O extends Options, R extends InvokerRequest<O>, C extends LookupInvoker.LookupInvokerContext<O, R, C>>
implements Invoker<R> {
/**
* 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<O>, C extends LookupInvokerContext<O, R, C>>
implements AutoCloseable {
public final LookupInvoker<O, R, C> invoker;
public final ProtoLookup protoLookup;
public final R invokerRequest;
public final Function<String, Path> cwdResolver;
public final Function<String, Path> installationResolver;
public final Function<String, Path> userResolver;
public final Session session;
protected LookupInvokerContext(LookupInvoker<O, R, C> 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<String, String> user = new HashMap<>(invokerRequest.userProperties());
user.put("session.rootDirectory", invokerRequest.rootDirectory().toString());
user.put("session.topDirectory", invokerRequest.topDirectory().toString());
Map<String, String> 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<String> 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<AutoCloseable> closeables = new ArrayList<>();
@Override
public void close() throws InvokerException {
List<Exception> causes = null;
List<AutoCloseable> 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<C extends LookupContext> implements Invoker {
protected final ProtoLookup protoLookup;
public LookupInvoker(ProtoLookup protoLookup) {
@ -200,19 +97,23 @@ public abstract class LookupInvoker<
}
@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 @@ public abstract class LookupInvoker<
return execute(context);
}
protected InvokerException handleException(LookupInvokerContext<O, R, C> 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 @@ public abstract class LookupInvoker<
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<String> sys = new HashSet<>(invokerRequest.systemProperties().keySet());
invokerRequest.userProperties().entrySet().stream()
.filter(k -> !sys.contains(k.getKey()))
@ -272,7 +172,7 @@ public abstract class LookupInvoker<
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<String, String> userProperties = invokerRequest.userProperties();
@ -338,7 +238,7 @@ public abstract class LookupInvoker<
}
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 @@ public abstract class LookupInvoker<
}
protected Consumer<String> 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 @@ public abstract class LookupInvoker<
}
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 @@ public abstract class LookupInvoker<
}
protected void helpOrVersionAndMayExit(C context) throws Exception {
R invokerRequest = context.invokerRequest;
InvokerRequest invokerRequest = context.invokerRequest;
if (invokerRequest.options().help().isPresent()) {
Consumer<String> 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<String> 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 @@ public abstract class LookupInvoker<
}
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 @@ public abstract class LookupInvoker<
.log(message);
}
protected ContainerCapsuleFactory<O, R, C> createContainerCapsuleFactory() {
protected ContainerCapsuleFactory<C> createContainerCapsuleFactory() {
return new PlexusContainerCapsuleFactory<>();
}
@ -487,7 +386,7 @@ public abstract class LookupInvoker<
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 @@ public abstract class LookupInvoker<
}
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 {

View File

@ -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 class PlexusContainerCapsule implements ContainerCapsule {
return lookup;
}
@Override
public Optional<ClassLoader> currentThreadClassLoader() {
return Optional.of(plexusContainer.getContainerRealm());
}
@Override
public void close() {
try {

View File

@ -33,7 +33,6 @@ import org.apache.maven.api.Constants;
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 @@ import static org.apache.maven.cling.invoker.Utils.toPlexusLoggingLevel;
/**
* Container capsule backed by Plexus Container.
*
* @param <O> the options type
* @param <R> the invoker request type
* @param <C> the invoker context type
* @param <C> The context type.
*/
public class PlexusContainerCapsuleFactory<
O extends Options, R extends InvokerRequest<O>, C extends LookupInvoker.LookupInvokerContext<O, R, C>>
implements ContainerCapsuleFactory<O, R, C> {
public class PlexusContainerCapsuleFactory<C extends LookupContext> implements ContainerCapsuleFactory<C> {
@Override
public ContainerCapsule createContainerCapsule(C context) throws InvokerException {
public ContainerCapsule createContainerCapsule(LookupInvoker<C> 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<C> invoker, C context) throws Exception {
ClassWorld classWorld = invoker.protoLookup.lookup(ClassWorld.class);
ClassRealm coreRealm = classWorld.getClassRealm("plexus.core");
List<Path> extClassPath = parseExtClasspath(context);
CoreExtensionEntry coreEntry = CoreExtensionEntry.discoverFrom(coreRealm);
List<CoreExtensionEntry> extensions = loadCoreExtensions(context, coreRealm, coreEntry.getExportedArtifacts());
List<CoreExtensionEntry> extensions =
loadCoreExtensions(invoker, context, coreRealm, coreEntry.getExportedArtifacts());
ClassRealm containerRealm =
setupContainerRealm(context.logger, classWorld, coreRealm, extClassPath, extensions);
ContainerConfiguration cc = new DefaultContainerConfiguration()
@ -108,11 +105,10 @@ public class PlexusContainerCapsuleFactory<
// 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<String, String> extensionSource = expression -> {
String value = invokerRequest.userProperties().get(expression);
if (value == null) {
@ -193,7 +189,7 @@ public class PlexusContainerCapsuleFactory<
protected void customizeContainer(C context, PlexusContainer container) throws Exception {}
protected List<Path> 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 @@ public class PlexusContainerCapsuleFactory<
}
protected List<CoreExtensionEntry> loadCoreExtensions(
C context, ClassRealm containerRealm, Set<String> providedArtifacts) throws Exception {
R invokerRequest = context.invokerRequest;
LookupInvoker<C> invoker, C context, ClassRealm containerRealm, Set<String> providedArtifacts)
throws Exception {
InvokerRequest invokerRequest = context.invokerRequest;
if (invokerRequest.coreExtensions().isEmpty()
|| invokerRequest.coreExtensions().get().isEmpty()) {
return Collections.emptyList();
@ -286,10 +283,10 @@ public class PlexusContainerCapsuleFactory<
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)

View File

@ -61,7 +61,7 @@ public class ProtoLogger implements Logger {
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);
}

View File

@ -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<MavenOptions, MavenInvokerRequest<MavenOptions>>
implements MavenParser<MavenInvokerRequest<MavenOptions>> {
@Override
protected DefaultMavenInvokerRequest<MavenOptions> 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<String> 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<MavenOptions> parsedOptions) {
return LayeredMavenOptions.layerMavenOptions(parsedOptions);
}
}

View File

@ -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;
}

View File

@ -34,9 +34,8 @@ import java.util.regex.Pattern;
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 org.eclipse.aether.transfer.TransferListener;
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<O>,
C extends DefaultMavenInvoker.MavenContext<O, R, C>>
extends LookupInvoker<O, R, C> implements MavenInvoker<R> {
@SuppressWarnings("VisibilityModifier")
public static class MavenContext<
O extends MavenOptions,
R extends MavenInvokerRequest<O>,
C extends DefaultMavenInvoker.MavenContext<O, R, C>>
extends LookupInvokerContext<O, R, C> {
protected MavenContext(DefaultMavenInvoker<O, R, C> 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 <C> The context type.
*/
public abstract class MavenInvoker<C extends MavenContext> extends LookupInvoker<C> {
public MavenInvoker(ProtoLookup protoLookup) {
super(protoLookup);
}
@ -136,14 +116,13 @@ public abstract class DefaultMavenInvoker<
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<O> invokerRequest = context.invokerRequest;
InvokerRequest invokerRequest = context.invokerRequest;
Map<String, Object> data = new HashMap<>();
data.put("plexus", context.lookup.lookup(PlexusContainer.class));
data.put("workingDirectory", invokerRequest.cwd().toString());
@ -157,11 +136,12 @@ public abstract class DefaultMavenInvoker<
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 @@ public abstract class DefaultMavenInvoker<
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 @@ public abstract class DefaultMavenInvoker<
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 @@ public abstract class DefaultMavenInvoker<
// 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 @@ public abstract class DefaultMavenInvoker<
//
// 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 @@ public abstract class DefaultMavenInvoker<
}
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 @@ public abstract class DefaultMavenInvoker<
}
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 @@ public abstract class DefaultMavenInvoker<
}
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 @@ public abstract class DefaultMavenInvoker<
}
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<String> optionValues = mavenOptions.projects().get();
@ -459,7 +440,7 @@ public abstract class DefaultMavenInvoker<
}
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<String> optionValues = mavenOptions.activatedProfiles().get();
@ -549,7 +530,7 @@ public abstract class DefaultMavenInvoker<
}
}
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 {

View File

@ -27,7 +27,6 @@ import java.util.Map;
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 @@ import static java.util.Objects.requireNonNull;
/**
* Maven execution request.
*
* @param <O> the options type this request carries
*/
public class DefaultMavenInvokerRequest<O extends MavenOptions> extends BaseInvokerRequest<O>
implements MavenInvokerRequest<O> {
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 class DefaultMavenInvokerRequest<O extends MavenOptions> extends BaseInvo
OutputStream out,
OutputStream err,
List<CoreExtension> coreExtensions,
O options) {
List<String> jvmArguments,
MavenOptions options) {
super(
parserRequest,
cwd,
@ -69,7 +66,8 @@ public class DefaultMavenInvokerRequest<O extends MavenOptions> extends BaseInvo
in,
out,
err,
coreExtensions);
coreExtensions,
jvmArguments);
this.options = requireNonNull(options);
}
@ -77,7 +75,7 @@ public class DefaultMavenInvokerRequest<O extends MavenOptions> extends BaseInvo
* The mandatory Maven options.
*/
@Nonnull
public O options() {
public MavenOptions options() {
return options;
}
}

View File

@ -26,19 +26,17 @@ import java.util.ArrayList;
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<O extends MavenOptions, R extends MavenInvokerRequest<O>> extends BaseParser<O, R>
implements MavenParser<R> {
public class MavenParser extends BaseParser {
@Override
protected List<O> parseCliOptions(LocalContext context) throws ParserException, IOException {
ArrayList<O> result = new ArrayList<>();
protected List<Options> parseCliOptions(LocalContext context) throws ParserException, IOException {
ArrayList<Options> result = new ArrayList<>();
// CLI args
result.add(parseMavenCliOptions(context.parserRequest.args()));
// maven.config; if exists
@ -49,15 +47,15 @@ public abstract class BaseMavenParser<O extends MavenOptions, R extends MavenInv
return result;
}
protected O parseMavenCliOptions(List<String> args) throws ParserException {
protected MavenOptions parseMavenCliOptions(List<String> 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<String> lines = Files.lines(configFile, Charset.defaultCharset())) {
List<String> 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 @@ public abstract class BaseMavenParser<O extends MavenOptions, R extends MavenInv
}
}
protected abstract O parseArgs(String source, List<String> args) throws ParserException;
protected MavenOptions parseArgs(String source, List<String> 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<Options> parsedOptions) {
return LayeredMavenOptions.layerMavenOptions((List) parsedOptions);
}
}

View File

@ -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<ExecutorRequest, Integer> exec;
public Context(
Properties properties,
URLClassLoader bootClassLoader,
String version,
Object classWorld,
ClassLoader tccl,
Function<ExecutorRequest, Integer> 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<Path, Context> 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<ExecutorRequest, Integer> 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<Exception> 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<URL> extraClasspath) {
ArrayList<URL> urls = new ArrayList<>(extraClasspath);
try (Stream<Path> 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";
}
}
}

View File

@ -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<MavenOptions>
implements ForkedMavenInvokerRequest {
private final List<String> jvmArguments;
@SuppressWarnings("ParameterNumber")
public DefaultForkedMavenInvokerRequest(
ParserRequest parserRequest,
Path cwd,
Path installationDirectory,
Path userHomeDirectory,
Map<String, String> userProperties,
Map<String, String> systemProperties,
Path topDirectory,
Path rootDirectory,
InputStream in,
OutputStream out,
OutputStream err,
List<CoreExtension> coreExtensions,
List<String> jvmArguments,
MavenOptions options) {
super(
parserRequest,
cwd,
installationDirectory,
userHomeDirectory,
userProperties,
systemProperties,
topDirectory,
rootDirectory,
in,
out,
err,
coreExtensions,
options);
this.jvmArguments = jvmArguments;
}
@Override
public Optional<List<String>> jvmArguments() {
return Optional.ofNullable(jvmArguments);
}
}

View File

@ -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<MavenOptions, ForkedMavenInvokerRequest>
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<String> 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<String> 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<MavenOptions> parsedOptions) {
return LayeredMavenOptions.layerMavenOptions(parsedOptions);
}
}

View File

@ -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<String> 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 {}
}

View File

@ -23,21 +23,21 @@ import java.util.ArrayList;
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 class DefaultForkedMavenInvoker implements ForkedMavenInvoker {
: invokerRequest.parserRequest().command())
.toString());
MavenOptions mavenOptions = invokerRequest.options();
MavenOptions mavenOptions = (MavenOptions) invokerRequest.options();
if (mavenOptions.userProperties().isPresent()) {
for (Map.Entry<String, String> entry :
mavenOptions.userProperties().get().entrySet()) {
@ -216,5 +216,5 @@ public class DefaultForkedMavenInvoker implements ForkedMavenInvoker {
}
}
protected void validate(ForkedMavenInvokerRequest invokerRequest) throws InvokerException {}
protected void validate(InvokerRequest invokerRequest) throws InvokerException {}
}

View File

@ -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<MavenOptions>, DefaultLocalMavenInvoker.LocalContext>
implements LocalMavenInvoker {
public static class LocalContext
extends DefaultMavenInvoker.MavenContext<
MavenOptions, MavenInvokerRequest<MavenOptions>, DefaultLocalMavenInvoker.LocalContext> {
protected LocalContext(DefaultLocalMavenInvoker invoker, MavenInvokerRequest<MavenOptions> invokerRequest) {
super(invoker, invokerRequest);
}
}
public DefaultLocalMavenInvoker(ProtoLookup protoLookup) {
super(protoLookup);
}
@Override
protected LocalContext createContext(MavenInvokerRequest<MavenOptions> invokerRequest) {
return new LocalContext(this, invokerRequest);
}
}

View File

@ -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<MavenInvokerRequest<MavenOptions>> {
/**
* Closes cleanly the daemon.
*/
public class LocalMavenInvoker extends MavenInvoker<MavenContext> {
public LocalMavenInvoker(ProtoLookup protoLookup) {
super(protoLookup);
}
@Override
void close() throws InvokerException;
protected MavenContext createContext(InvokerRequest invokerRequest) throws InvokerException {
return new MavenContext(invokerRequest);
}
}

View File

@ -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;

View File

@ -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<MavenOptions>, DefaultResidentMavenInvoker.LocalContext>
implements ResidentMavenInvoker {
public static class LocalContext
extends DefaultMavenInvoker.MavenContext<
MavenOptions, MavenInvokerRequest<MavenOptions>, DefaultResidentMavenInvoker.LocalContext> {
protected LocalContext(DefaultResidentMavenInvoker invoker, MavenInvokerRequest<MavenOptions> 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<MavenOptions> 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<String, LocalContext> residentContext;
public DefaultResidentMavenInvoker(ProtoLookup protoLookup) {
super(protoLookup);
this.residentContext = new ConcurrentHashMap<>();
}
@Override
public void close() throws InvokerException {
ArrayList<InvokerException> 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<MavenOptions> invokerRequest) {
return residentContext
.computeIfAbsent(getContextId(invokerRequest), k -> new LocalContext(this, invokerRequest))
.copy(invokerRequest);
}
protected String getContextId(MavenInvokerRequest<MavenOptions> 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);
}
}
}

View File

@ -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;
}
}

View File

@ -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<ResidentMavenContext> {
private final ConcurrentHashMap<String, ResidentMavenContext> residentContext;
public ResidentMavenInvoker(ProtoLookup protoLookup) {
super(protoLookup);
this.residentContext = new ConcurrentHashMap<>();
}
@Override
public void close() throws InvokerException {
ArrayList<InvokerException> 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);
}
}
}

View File

@ -35,6 +35,9 @@ import org.codehaus.plexus.interpolation.InterpolationException;
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();

View File

@ -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<String, Goal> goals;
public List<AttributedString> 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());
}
}

View File

@ -20,75 +20,41 @@ package org.apache.maven.cling.invoker.mvnenc;
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<EncryptOptions, EncryptInvokerRequest, DefaultEncryptInvoker.LocalContext>
implements EncryptInvoker {
public class EncryptInvoker extends LookupInvoker<EncryptContext> {
@SuppressWarnings("VisibilityModifier")
public static class LocalContext
extends LookupInvokerContext<EncryptOptions, EncryptInvokerRequest, DefaultEncryptInvoker.LocalContext> {
protected LocalContext(DefaultEncryptInvoker invoker, EncryptInvokerRequest invokerRequest) {
super(invoker, invokerRequest);
}
public Map<String, Goal> goals;
public List<AttributedString> 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 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 @@ public class DefaultEncryptInvoker
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 @@ public class DefaultEncryptInvoker
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 @@ public class DefaultEncryptInvoker
}
}
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.");

View File

@ -25,20 +25,18 @@ import java.util.List;
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<EncryptOptions> 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 class DefaultEncryptInvokerRequest extends BaseInvokerRequest<EncryptOpti
OutputStream out,
OutputStream err,
List<CoreExtension> coreExtensions,
Options options) {
List<String> jvmArguments,
EncryptOptions options) {
super(
parserRequest,
cwd,
@ -64,8 +63,9 @@ public class DefaultEncryptInvokerRequest extends BaseInvokerRequest<EncryptOpti
in,
out,
err,
coreExtensions);
this.options = (EncryptOptions) requireNonNull(options);
coreExtensions,
jvmArguments);
this.options = requireNonNull(options);
}
/**

View File

@ -22,16 +22,15 @@ import java.util.Collections;
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<EncryptOptions, EncryptInvokerRequest> 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 @@ public class DefaultEncryptParser extends BaseParser<EncryptOptions, EncryptInvo
context.parserRequest.out(),
context.parserRequest.err(),
context.extensions,
context.options);
getJvmArguments(context.rootDirectory),
(EncryptOptions) context.options);
}
@Override
protected List<EncryptOptions> parseCliOptions(LocalContext context) throws ParserException {
protected List<Options> parseCliOptions(LocalContext context) throws ParserException {
return Collections.singletonList(parseEncryptCliOptions(context.parserRequest.args()));
}
@ -61,7 +61,7 @@ public class DefaultEncryptParser extends BaseParser<EncryptOptions, EncryptInvo
}
@Override
protected EncryptOptions assembleOptions(List<EncryptOptions> parsedOptions) {
protected Options assembleOptions(List<Options> parsedOptions) {
// nothing to assemble, we deal with CLI only
return parsedOptions.get(0);
}

View File

@ -22,5 +22,5 @@ package org.apache.maven.cling.invoker.mvnenc;
* The mvnenc tool goal.
*/
public interface Goal {
int execute(DefaultEncryptInvoker.LocalContext context) throws Exception;
int execute(EncryptContext context) throws Exception;
}

View File

@ -23,10 +23,10 @@ import java.util.Map;
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 @@ public abstract class ConfiguredGoalSupport extends GoalSupport {
}
@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 abstract class ConfiguredGoalSupport extends GoalSupport {
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 @@ public abstract class ConfiguredGoalSupport extends GoalSupport {
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 @@ public abstract class ConfiguredGoalSupport extends GoalSupport {
}
}
protected abstract int doExecute(DefaultEncryptInvoker.LocalContext context) throws Exception;
protected abstract int doExecute(EncryptContext context) throws Exception;
}

View File

@ -23,11 +23,11 @@ import javax.inject.Named;
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 class Decrypt extends ConfiguredGoalSupport {
}
@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));

View File

@ -23,10 +23,10 @@ import javax.inject.Named;
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 class Diag extends ConfiguredGoalSupport {
}
@Override
protected int doExecute(DefaultEncryptInvoker.LocalContext context) {
protected int doExecute(EncryptContext context) {
dumpResponse(context, "", secDispatcher.validateConfiguration());
return OK;
}

View File

@ -23,10 +23,10 @@ import javax.inject.Named;
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 class Encrypt extends ConfiguredGoalSupport {
}
@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;

View File

@ -26,8 +26,9 @@ import java.util.List;
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.LineReader;
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 class Init extends GoalSupport {
}
@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 @@ public class Init extends GoalSupport {
}
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());

View File

@ -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<ForkedMavenInvokerRequest> {}
package org.apache.maven.cling.invoker.mvnenc;

View File

@ -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;

View File

@ -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<String> 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();
}

View File

@ -27,15 +27,13 @@ import java.util.List;
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<O extends MavenOptions, R extends MavenInvokerRequest<O>> {
public abstract class MavenInvokerTestSupport {
protected void invoke(Path cwd, Collection<String> goals) throws Exception {
// works only in recent Maven4
@ -46,75 +44,30 @@ public abstract class MavenInvokerTestSupport<O extends MavenOptions, R extends
"${maven.home}/conf/maven.properties must be a file");
Files.createDirectory(cwd.resolve(".mvn"));
String pomString =
"""
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.apache.maven.samples</groupId>
<artifactId>sample</artifactId>
<version>1.0.0</version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.junit</groupId>
<artifactId>junit-bom</artifactId>
<version>5.11.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
""";
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<R> parser = createParser();
try (Invoker<R> 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<R> createInvoker();
protected abstract Invoker createInvoker();
protected abstract Parser<R> createParser();
protected abstract Parser createParser();
}

View File

@ -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 =
"""
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.apache.maven.samples</groupId>
<artifactId>sample</artifactId>
<version>1.0.0</version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.junit</groupId>
<artifactId>junit-bom</artifactId>
<version>5.11.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
""";
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!");
}
}
""";
}

View File

@ -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"));
}
}

View File

@ -23,9 +23,9 @@ import java.util.Arrays;
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 @@ import org.junit.jupiter.api.io.TempDir;
/**
* Forked UT: it cannot use jimFS as it runs in child process.
*/
public class DefaultForkedMavenInvokerTest extends MavenInvokerTestSupport<MavenOptions, ForkedMavenInvokerRequest> {
@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<ForkedMavenInvokerRequest> createInvoker() {
return new DefaultForkedMavenInvoker();
protected Invoker createInvoker() {
return new ForkedMavenInvoker();
}
@Override
protected Parser<ForkedMavenInvokerRequest> createParser() {
return new DefaultForkedMavenParser();
protected Parser createParser() {
return new MavenParser();
}
@Test

View File

@ -26,11 +26,9 @@ import com.google.common.jimfs.Configuration;
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 @@ import org.junit.jupiter.api.io.TempDir;
*/
@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<MavenOptions, MavenInvokerRequest<MavenOptions>> {
public class DefaultLocalMavenInvokerTest extends MavenInvokerTestSupport {
@Override
protected Invoker<MavenInvokerRequest<MavenOptions>> 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<MavenInvokerRequest<MavenOptions>> createParser() {
return new DefaultMavenParser();
protected Parser createParser() {
return new MavenParser();
}
@Test

View File

@ -26,11 +26,9 @@ import com.google.common.jimfs.Configuration;
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 @@ import org.junit.jupiter.api.io.TempDir;
*/
@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<MavenOptions, MavenInvokerRequest<MavenOptions>> {
public class DefaultResidentMavenInvokerTest extends MavenInvokerTestSupport {
@Override
protected Invoker<MavenInvokerRequest<MavenOptions>> 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<MavenInvokerRequest<MavenOptions>> createParser() {
return new DefaultMavenParser();
protected Parser createParser() {
return new MavenParser();
}
@Test