[MNG-8283] More mvnd related changes (#1775)

Changes:
* there is only one parser for "just maven", no need for 3
* aligned scopes (public) of local context for simplicity, we can fix visibility later
* allow custom guice modules (unused, may undo this)
* split logging setup in two steps: config and activate

---

https://issues.apache.org/jira/browse/MNG-8283
This commit is contained in:
Tamas Cservenak 2024-10-04 17:33:25 +02:00 committed by GitHub
parent 533790bb4a
commit 60ae468a2e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 174 additions and 633 deletions

View File

@ -1,32 +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.local;
import org.apache.maven.api.annotations.Experimental;
import org.apache.maven.api.cli.mvn.MavenInvokerRequest;
import org.apache.maven.api.cli.mvn.MavenOptions;
import org.apache.maven.api.cli.mvn.MavenParser;
/**
* Local parser.
*
* @since 4.0.0
*/
@Experimental
public interface LocalMavenParser extends MavenParser<MavenInvokerRequest<MavenOptions>> {}

View File

@ -21,6 +21,8 @@ package org.apache.maven.api.cli.mvn.resident;
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;
/**
* Resident invoker. Instance is shut down when this instance is closed.
@ -28,7 +30,7 @@ import org.apache.maven.api.cli.mvn.MavenInvoker;
* @since 4.0.0
*/
@Experimental
public interface ResidentMavenInvoker extends MavenInvoker<ResidentMavenInvokerRequest> {
public interface ResidentMavenInvoker extends MavenInvoker<MavenInvokerRequest<MavenOptions>> {
/**
* Closes cleanly the daemon.
*/

View File

@ -1,36 +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.resident;
import java.util.List;
import java.util.Optional;
import org.apache.maven.api.annotations.Nonnull;
import org.apache.maven.api.cli.mvn.MavenInvokerRequest;
public interface ResidentMavenInvokerRequest extends MavenInvokerRequest<ResidentMavenOptions> {
/**
* 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,50 +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.resident;
import java.util.Collection;
import java.util.Map;
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.MavenOptions;
/**
* Resident options.
*/
@Experimental
public interface ResidentMavenOptions extends MavenOptions {
/**
* Indicates whether raw streams should be used with daemon.
*
* @return an {@link Optional} containing true if raw streams are enabled, false if disabled, or empty if not specified
*/
@Nonnull
Optional<Boolean> rawStreams();
/**
* Returns a new instance of {@link MavenOptions} with values interpolated using the given properties.
*
* @param properties a collection of property maps to use for interpolation
* @return a new MavenOptions instance with interpolated values
*/
@Nonnull
ResidentMavenOptions interpolate(@Nonnull Collection<Map<String, String>> properties);
}

View File

@ -1,30 +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.resident;
import org.apache.maven.api.annotations.Experimental;
import org.apache.maven.api.cli.mvn.MavenParser;
/**
* Resident parser.
*
* @since 4.0.0
*/
@Experimental
public interface ResidentMavenParser extends MavenParser<ResidentMavenInvokerRequest> {}

View File

@ -26,8 +26,8 @@ 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.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.local.DefaultLocalMavenParser;
import org.apache.maven.jline.JLineMessageBuilderFactory;
import org.codehaus.plexus.classworlds.ClassWorld;
@ -67,6 +67,6 @@ public class MavenCling extends ClingSupport<MavenOptions, MavenInvokerRequest<M
@Override
protected MavenInvokerRequest<MavenOptions> parseArguments(String[] args) throws ParserException, IOException {
return new DefaultLocalMavenParser().mvn(args, new ProtoLogger(), new JLineMessageBuilderFactory());
return new DefaultMavenParser().mvn(args, new ProtoLogger(), new JLineMessageBuilderFactory());
}
}

View File

@ -121,6 +121,7 @@ public abstract class LookupInvoker<
public Logger logger;
public ILoggerFactory loggerFactory;
public Slf4jConfiguration slf4jConfiguration;
public Slf4jConfiguration.Level loggerLevel;
public ContainerCapsule containerCapsule;
public Lookup lookup;
@ -156,6 +157,7 @@ public abstract class LookupInvoker<
validate(context);
prepare(context);
logging(context);
activateLogging(context);
if (invokerRequest.options().help().isPresent()) {
invokerRequest.options().displayHelp(context.invokerRequest.parserRequest(), context.stdOut);
@ -231,7 +233,7 @@ public abstract class LookupInvoker<
}
context.loggerFactory = LoggerFactory.getILoggerFactory();
Slf4jConfiguration slf4jConfiguration = Slf4jConfigurationFactory.getConfiguration(context.loggerFactory);
context.slf4jConfiguration = Slf4jConfigurationFactory.getConfiguration(context.loggerFactory);
context.loggerLevel = Slf4jConfiguration.Level.INFO;
if (mavenOptions.verbose().orElse(false)) {
@ -239,7 +241,7 @@ public abstract class LookupInvoker<
} else if (mavenOptions.quiet().orElse(false)) {
context.loggerLevel = Slf4jConfiguration.Level.ERROR;
}
slf4jConfiguration.setRootLoggerLevel(context.loggerLevel);
context.slf4jConfiguration.setRootLoggerLevel(context.loggerLevel);
// else fall back to default log level specified in conf
// see https://issues.apache.org/jira/browse/MNG-2570
@ -255,8 +257,13 @@ public abstract class LookupInvoker<
throw new InvokerException("Cannot set up log " + e.getMessage(), e);
}
}
}
slf4jConfiguration.activate();
protected void activateLogging(C context) throws Exception {
R invokerRequest = context.invokerRequest;
Options mavenOptions = invokerRequest.options();
context.slf4jConfiguration.activate();
org.slf4j.Logger l = context.loggerFactory.getLogger(this.getClass().getName());
context.logger = (level, message, error) -> l.atLevel(org.slf4j.event.Level.valueOf(level.name()))
.setCause(error)

View File

@ -28,6 +28,7 @@ import java.util.Set;
import java.util.function.Function;
import com.google.inject.AbstractModule;
import com.google.inject.Module;
import org.apache.maven.api.Constants;
import org.apache.maven.api.cli.InvokerException;
import org.apache.maven.api.cli.InvokerRequest;
@ -103,16 +104,9 @@ public class PlexusContainerCapsuleFactory<
exportedArtifacts.addAll(extension.getExportedArtifacts());
exportedPackages.addAll(extension.getExportedPackages());
}
final CoreExports exports = new CoreExports(containerRealm, exportedArtifacts, exportedPackages);
CoreExports exports = new CoreExports(containerRealm, exportedArtifacts, exportedPackages);
Thread.currentThread().setContextClassLoader(containerRealm);
DefaultPlexusContainer container = new DefaultPlexusContainer(cc, new AbstractModule() {
@Override
protected void configure() {
bind(ILoggerFactory.class).toInstance(context.loggerFactory);
bind(CoreExports.class).toInstance(exports);
bind(MessageBuilderFactory.class).toInstance(context.invokerRequest.messageBuilderFactory());
}
});
DefaultPlexusContainer container = new DefaultPlexusContainer(cc, getCustomModule(context, exports));
// NOTE: To avoid inconsistencies, we'll use the TCCL exclusively for lookups
container.setLookupRealm(null);
@ -157,6 +151,22 @@ public class PlexusContainerCapsuleFactory<
return container;
}
/**
* Note: overriding this method should be avoided. Preferred way to replace Maven components is the "normal" way
* where the components are on index (are annotated with JSR330 annotations and Sisu index is created) and, they
* have priorities set.
*/
protected Module getCustomModule(C context, CoreExports exports) {
return new AbstractModule() {
@Override
protected void configure() {
bind(ILoggerFactory.class).toInstance(context.loggerFactory);
bind(CoreExports.class).toInstance(exports);
bind(MessageBuilderFactory.class).toInstance(context.invokerRequest.messageBuilderFactory());
}
};
}
protected void customizeContainerConfiguration(C context, ContainerConfiguration configuration) throws Exception {}
protected void customizeContainer(C context, PlexusContainer container) throws Exception {}

View File

@ -0,0 +1,87 @@
/*
* 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.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
import org.apache.maven.api.cli.Options;
import org.apache.maven.api.cli.ParserException;
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.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> {
@SuppressWarnings("ParameterNumber")
@Override
protected abstract R getInvokerRequest(
ParserRequest parserRequest,
Path cwd,
Path installationDirectory,
Path userHomeDirectory,
Map<String, String> userProperties,
Map<String, String> systemProperties,
Path topDirectory,
Path rootDirectory,
ArrayList<CoreExtension> extensions,
Options options);
@Override
protected List<O> parseCliOptions(Path rootDirectory, List<String> args) throws ParserException, IOException {
ArrayList<O> result = new ArrayList<>();
// CLI args
result.add(parseMavenCliOptions(args));
// maven.config; if exists
Path mavenConfig = rootDirectory.resolve(".mvn/maven.config");
if (Files.isRegularFile(mavenConfig)) {
result.add(parseMavenConfigOptions(mavenConfig));
}
return result;
}
protected O parseMavenCliOptions(List<String> args) throws ParserException {
return parseArgs(Options.SOURCE_CLI, args);
}
protected O 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);
if (options.goals().isPresent()) {
// This file can only contain options, not args (goals or phases)
throw new ParserException("Unrecognized maven.config file entries: "
+ options.goals().get());
}
return options;
}
}
protected abstract O parseArgs(String source, List<String> args) throws ParserException;
}

View File

@ -76,7 +76,7 @@ public abstract class DefaultMavenInvoker<
extends LookupInvoker<O, R, C> implements MavenInvoker<R> {
@SuppressWarnings("VisibilityModifier")
protected static class MavenContext<
public static class MavenContext<
O extends MavenOptions,
R extends MavenInvokerRequest<O>,
C extends DefaultMavenInvoker.MavenContext<O, R, C>>

View File

@ -18,15 +18,12 @@
*/
package org.apache.maven.cling.invoker.mvn;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
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.ParserRequest;
@ -34,13 +31,12 @@ 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.api.cli.mvn.MavenParser;
import org.apache.maven.cling.invoker.BaseParser;
public abstract class DefaultMavenParser<O extends MavenOptions, R extends MavenInvokerRequest<O>>
extends BaseParser<O, R> implements MavenParser<R> {
public class DefaultMavenParser extends BaseMavenParser<MavenOptions, MavenInvokerRequest<MavenOptions>>
implements MavenParser<MavenInvokerRequest<MavenOptions>> {
@SuppressWarnings("ParameterNumber")
@Override
protected abstract R getInvokerRequest(
protected DefaultMavenInvokerRequest<MavenOptions> getInvokerRequest(
ParserRequest parserRequest,
Path cwd,
Path installationDirectory,
@ -50,38 +46,34 @@ public abstract class DefaultMavenParser<O extends MavenOptions, R extends Maven
Path topDirectory,
Path rootDirectory,
ArrayList<CoreExtension> extensions,
Options options);
Options options) {
return new DefaultMavenInvokerRequest<>(
parserRequest,
cwd,
installationDirectory,
userHomeDirectory,
userProperties,
systemProperties,
topDirectory,
rootDirectory,
parserRequest.in(),
parserRequest.out(),
parserRequest.err(),
extensions,
(MavenOptions) options);
}
@Override
protected List<O> parseCliOptions(Path rootDirectory, List<String> args) throws ParserException, IOException {
ArrayList<O> result = new ArrayList<>();
// CLI args
result.add(parseMavenCliOptions(args));
// maven.config; if exists
Path mavenConfig = rootDirectory.resolve(".mvn/maven.config");
if (Files.isRegularFile(mavenConfig)) {
result.add(parseMavenConfigOptions(mavenConfig));
}
return result;
}
protected O parseMavenCliOptions(List<String> args) throws ParserException {
return parseArgs(Options.SOURCE_CLI, args);
}
protected O 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);
if (options.goals().isPresent()) {
// This file can only contain options, not args (goals or phases)
throw new ParserException("Unrecognized maven.config file entries: "
+ options.goals().get());
}
return options;
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());
}
}
protected abstract O parseArgs(String source, List<String> args) throws ParserException;
@Override
protected MavenOptions assembleOptions(List<MavenOptions> parsedOptions) {
return LayeredMavenOptions.layerMavenOptions(parsedOptions);
}
}

View File

@ -31,11 +31,11 @@ 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.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.DefaultMavenParser;
import org.apache.maven.cling.invoker.mvn.LayeredMavenOptions;
public class DefaultForkedMavenParser extends DefaultMavenParser<MavenOptions, ForkedMavenInvokerRequest>
public class DefaultForkedMavenParser extends BaseMavenParser<MavenOptions, ForkedMavenInvokerRequest>
implements ForkedMavenParser {
@SuppressWarnings("ParameterNumber")
@ -76,6 +76,7 @@ public class DefaultForkedMavenParser extends DefaultMavenParser<MavenOptions, F
return null;
}
// TODO: same is in DefaultMavenParser!!! (duplication)
@Override
protected MavenOptions parseArgs(String source, List<String> args) throws ParserException {
try {
@ -85,6 +86,7 @@ public class DefaultForkedMavenParser extends DefaultMavenParser<MavenOptions, F
}
}
// TODO: same is in DefaultMavenParser!!! (duplication)
@Override
protected MavenOptions assembleOptions(List<MavenOptions> parsedOptions) {
return LayeredMavenOptions.layerMavenOptions(parsedOptions);

View File

@ -29,7 +29,7 @@ public class DefaultLocalMavenInvoker
MavenOptions, MavenInvokerRequest<MavenOptions>, DefaultLocalMavenInvoker.LocalContext>
implements LocalMavenInvoker {
protected static class LocalContext
public static class LocalContext
extends DefaultMavenInvoker.MavenContext<
MavenOptions, MavenInvokerRequest<MavenOptions>, DefaultLocalMavenInvoker.LocalContext> {
protected LocalContext(DefaultLocalMavenInvoker invoker, MavenInvokerRequest<MavenOptions> invokerRequest) {

View File

@ -1,83 +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 java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
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.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.api.cli.mvn.local.LocalMavenParser;
import org.apache.maven.cling.invoker.mvn.CommonsCliMavenOptions;
import org.apache.maven.cling.invoker.mvn.DefaultMavenInvokerRequest;
import org.apache.maven.cling.invoker.mvn.DefaultMavenParser;
import org.apache.maven.cling.invoker.mvn.LayeredMavenOptions;
public class DefaultLocalMavenParser extends DefaultMavenParser<MavenOptions, MavenInvokerRequest<MavenOptions>>
implements LocalMavenParser {
@SuppressWarnings("ParameterNumber")
@Override
protected DefaultMavenInvokerRequest<MavenOptions> getInvokerRequest(
ParserRequest parserRequest,
Path cwd,
Path installationDirectory,
Path userHomeDirectory,
Map<String, String> userProperties,
Map<String, String> systemProperties,
Path topDirectory,
Path rootDirectory,
ArrayList<CoreExtension> extensions,
Options options) {
return new DefaultMavenInvokerRequest<>(
parserRequest,
cwd,
installationDirectory,
userHomeDirectory,
userProperties,
systemProperties,
topDirectory,
rootDirectory,
parserRequest.in(),
parserRequest.out(),
parserRequest.err(),
extensions,
(MavenOptions) 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.getCause());
}
}
@Override
protected MavenOptions assembleOptions(List<MavenOptions> parsedOptions) {
return LayeredMavenOptions.layerMavenOptions(parsedOptions);
}
}

View File

@ -1,100 +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.Collection;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Optional;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.maven.api.cli.mvn.resident.ResidentMavenOptions;
import org.apache.maven.cling.invoker.mvn.CommonsCliMavenOptions;
import org.codehaus.plexus.interpolation.BasicInterpolator;
import org.codehaus.plexus.interpolation.InterpolationException;
import static org.apache.maven.cling.invoker.Utils.createInterpolator;
public class CommonsCliResidentMavenOptions extends CommonsCliMavenOptions implements ResidentMavenOptions {
public static CommonsCliResidentMavenOptions parse(String source, String[] args) throws ParseException {
CLIManager cliManager = new CLIManager();
return new CommonsCliResidentMavenOptions(source, cliManager, cliManager.parse(args));
}
protected CommonsCliResidentMavenOptions(String source, CLIManager cliManager, CommandLine commandLine) {
super(source, cliManager, commandLine);
}
@Override
public Optional<Boolean> rawStreams() {
if (commandLine.hasOption(CLIManager.RAW_STREAMS)) {
return Optional.of(Boolean.TRUE);
}
return Optional.empty();
}
private static CommonsCliResidentMavenOptions interpolate(
CommonsCliResidentMavenOptions options, Collection<Map<String, String>> properties) {
try {
// now that we have properties, interpolate all arguments
BasicInterpolator interpolator = createInterpolator(properties);
CommandLine.Builder commandLineBuilder = new CommandLine.Builder();
commandLineBuilder.setDeprecatedHandler(o -> {});
for (Option option : options.commandLine.getOptions()) {
if (!CLIManager.USER_PROPERTY.equals(option.getOpt())) {
List<String> values = option.getValuesList();
for (ListIterator<String> it = values.listIterator(); it.hasNext(); ) {
it.set(interpolator.interpolate(it.next()));
}
}
commandLineBuilder.addOption(option);
}
for (String arg : options.commandLine.getArgList()) {
commandLineBuilder.addArg(interpolator.interpolate(arg));
}
return new CommonsCliResidentMavenOptions(
options.source, (CLIManager) options.cliManager, commandLineBuilder.build());
} catch (InterpolationException e) {
throw new IllegalArgumentException("Could not interpolate CommonsCliOptions", e);
}
}
@Override
public ResidentMavenOptions interpolate(Collection<Map<String, String>> properties) {
return interpolate(this, properties);
}
protected static class CLIManager extends CommonsCliMavenOptions.CLIManager {
public static final String RAW_STREAMS = "ras";
@Override
protected void prepareOptions(Options options) {
super.prepareOptions(options);
options.addOption(Option.builder(RAW_STREAMS)
.longOpt("raw-streams")
.hasArg()
.desc("Use raw-streams for daemon communication")
.build());
}
}
}

View File

@ -22,9 +22,9 @@ 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.api.cli.mvn.resident.ResidentMavenInvokerRequest;
import org.apache.maven.api.cli.mvn.resident.ResidentMavenOptions;
import org.apache.maven.cling.invoker.ProtoLookup;
import org.apache.maven.cling.invoker.mvn.DefaultMavenInvoker;
@ -34,14 +34,14 @@ import org.apache.maven.cling.invoker.mvn.DefaultMavenInvoker;
*/
public class DefaultResidentMavenInvoker
extends DefaultMavenInvoker<
ResidentMavenOptions, ResidentMavenInvokerRequest, DefaultResidentMavenInvoker.LocalContext>
MavenOptions, MavenInvokerRequest<MavenOptions>, DefaultResidentMavenInvoker.LocalContext>
implements ResidentMavenInvoker {
protected static class LocalContext
public static class LocalContext
extends DefaultMavenInvoker.MavenContext<
ResidentMavenOptions, ResidentMavenInvokerRequest, DefaultResidentMavenInvoker.LocalContext> {
MavenOptions, MavenInvokerRequest<MavenOptions>, DefaultResidentMavenInvoker.LocalContext> {
protected LocalContext(DefaultResidentMavenInvoker invoker, ResidentMavenInvokerRequest invokerRequest) {
protected LocalContext(DefaultResidentMavenInvoker invoker, MavenInvokerRequest<MavenOptions> invokerRequest) {
super(invoker, invokerRequest);
}
@ -54,7 +54,7 @@ public class DefaultResidentMavenInvoker
super.close();
}
public LocalContext copy(ResidentMavenInvokerRequest invokerRequest) {
public LocalContext copy(MavenInvokerRequest<MavenOptions> invokerRequest) {
LocalContext shadow = new LocalContext((DefaultResidentMavenInvoker) invoker, invokerRequest);
shadow.logger = logger;
@ -107,7 +107,7 @@ public class DefaultResidentMavenInvoker
}
@Override
protected LocalContext createContext(ResidentMavenInvokerRequest invokerRequest) {
protected LocalContext createContext(MavenInvokerRequest<MavenOptions> invokerRequest) {
return residentContext
.computeIfAbsent(getContextId(invokerRequest), k -> {
LocalContext master = new LocalContext(this, invokerRequest);
@ -125,7 +125,7 @@ public class DefaultResidentMavenInvoker
.copy(invokerRequest);
}
protected String getContextId(ResidentMavenInvokerRequest 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";

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.resident;
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.resident.ResidentMavenInvokerRequest;
import org.apache.maven.api.cli.mvn.resident.ResidentMavenOptions;
import org.apache.maven.cling.invoker.mvn.DefaultMavenInvokerRequest;
/**
* Maven execution request.
*/
public class DefaultResidentMavenInvokerRequest extends DefaultMavenInvokerRequest<ResidentMavenOptions>
implements ResidentMavenInvokerRequest {
private final List<String> jvmArguments;
@SuppressWarnings("ParameterNumber")
public DefaultResidentMavenInvokerRequest(
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,
ResidentMavenOptions 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.of(jvmArguments);
}
}

View File

@ -1,91 +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.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
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.ParserRequest;
import org.apache.maven.api.cli.extensions.CoreExtension;
import org.apache.maven.api.cli.mvn.resident.ResidentMavenInvokerRequest;
import org.apache.maven.api.cli.mvn.resident.ResidentMavenOptions;
import org.apache.maven.api.cli.mvn.resident.ResidentMavenParser;
import org.apache.maven.cling.invoker.mvn.DefaultMavenParser;
public class DefaultResidentMavenParser extends DefaultMavenParser<ResidentMavenOptions, ResidentMavenInvokerRequest>
implements ResidentMavenParser {
@SuppressWarnings("ParameterNumber")
@Override
protected DefaultResidentMavenInvokerRequest getInvokerRequest(
ParserRequest parserRequest,
Path cwd,
Path installationDirectory,
Path userHomeDirectory,
Map<String, String> userProperties,
Map<String, String> systemProperties,
Path topDirectory,
Path rootDirectory,
ArrayList<CoreExtension> extensions,
Options options) {
return new DefaultResidentMavenInvokerRequest(
parserRequest,
cwd,
installationDirectory,
userHomeDirectory,
userProperties,
systemProperties,
topDirectory,
rootDirectory,
parserRequest.in(),
parserRequest.out(),
parserRequest.err(),
extensions,
getJvmArguments(rootDirectory),
(ResidentMavenOptions) options);
}
protected List<String> getJvmArguments(Path rootDirectory) {
if (rootDirectory != null) {
// TODO: do this
return Collections.emptyList();
}
return null;
}
@Override
protected ResidentMavenOptions parseArgs(String source, List<String> args) throws ParserException {
try {
return CommonsCliResidentMavenOptions.parse(source, args.toArray(new String[0]));
} catch (ParseException e) {
throw new ParserException("Failed to parse source " + source, e.getCause());
}
}
@Override
protected ResidentMavenOptions assembleOptions(List<ResidentMavenOptions> parsedOptions) {
return LayeredResidentMavenOptions.layerResidentMavenOptions(parsedOptions);
}
}

View File

@ -1,61 +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.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import org.apache.maven.api.cli.mvn.resident.ResidentMavenOptions;
import org.apache.maven.cling.invoker.mvn.LayeredMavenOptions;
public class LayeredResidentMavenOptions extends LayeredMavenOptions<ResidentMavenOptions>
implements ResidentMavenOptions {
public static ResidentMavenOptions layerResidentMavenOptions(Collection<ResidentMavenOptions> options) {
List<ResidentMavenOptions> o = options.stream().filter(Objects::nonNull).toList();
if (o.isEmpty()) {
throw new IllegalArgumentException("No options specified (or all were null)");
} else if (o.size() == 1) {
return o.get(0);
} else {
return new LayeredResidentMavenOptions(o);
}
}
private LayeredResidentMavenOptions(List<ResidentMavenOptions> options) {
super(options);
}
@Override
public Optional<Boolean> rawStreams() {
return Optional.empty();
}
@Override
public ResidentMavenOptions interpolate(Collection<Map<String, String>> properties) {
ArrayList<ResidentMavenOptions> interpolatedOptions = new ArrayList<>(options.size());
for (ResidentMavenOptions o : options) {
interpolatedOptions.add(o.interpolate(properties));
}
return layerResidentMavenOptions(interpolatedOptions);
}
}

View File

@ -29,6 +29,7 @@ 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.codehaus.plexus.classworlds.ClassWorld;
import org.junit.jupiter.api.Disabled;
@ -50,7 +51,7 @@ public class DefaultLocalMavenInvokerTest
@Override
protected Parser<MavenInvokerRequest<MavenOptions>> createParser() {
return new DefaultLocalMavenParser();
return new DefaultMavenParser();
}
@Test

View File

@ -26,9 +26,10 @@ 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.resident.ResidentMavenInvokerRequest;
import org.apache.maven.api.cli.mvn.resident.ResidentMavenOptions;
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.codehaus.plexus.classworlds.ClassWorld;
import org.junit.jupiter.api.Disabled;
@ -40,18 +41,18 @@ import org.junit.jupiter.api.io.TempDir;
* Resident UT.
*/
public class DefaultResidentMavenInvokerTest
extends MavenInvokerTestSupport<ResidentMavenOptions, ResidentMavenInvokerRequest> {
extends MavenInvokerTestSupport<MavenOptions, MavenInvokerRequest<MavenOptions>> {
@Override
protected Invoker<ResidentMavenInvokerRequest> createInvoker() {
protected Invoker<MavenInvokerRequest<MavenOptions>> createInvoker() {
return new DefaultResidentMavenInvoker(ProtoLookup.builder()
.addMapping(ClassWorld.class, new ClassWorld("plexus.core", ClassLoader.getSystemClassLoader()))
.build());
}
@Override
protected Parser<ResidentMavenInvokerRequest> createParser() {
return new DefaultResidentMavenParser();
protected Parser<MavenInvokerRequest<MavenOptions>> createParser() {
return new DefaultMavenParser();
}
@Test