Skip to content

Commit

Permalink
Support core extension more configuration
Browse files Browse the repository at this point in the history
Signed-off-by: crazyhzm <[email protected]>
CrazyHZM committed Dec 7, 2024
1 parent b0a210d commit 6858c52
Showing 6 changed files with 103 additions and 66 deletions.
2 changes: 1 addition & 1 deletion README.adoc
Original file line number Diff line number Diff line change
@@ -185,7 +185,7 @@ A few special properties do not follow the above mechanism:
* `mvnd.daemonStorage`: this property defines the location where mvnd stores its files (registry and daemon logs). This property can only be defined as a system property on the command line
* `mvnd.id`: this property is used internally to identify the daemon being created
* `mvnd.extClasspath`: internal option to specify the maven extension classpath
* `mvnd.coreExtensions`: internal option to specify the list of maven extension to register
* `mvnd.coreExtensionFilePath`: internal option to specify the maven extension configuration file path

For a full list of available properties please see
https://github.com/apache/maven-mvnd/blob/master/dist/src/main/distro/conf/mvnd.properties[/dist/src/main/distro/conf/mvnd.properties].
Original file line number Diff line number Diff line change
@@ -18,8 +18,6 @@
*/
package org.mvndaemon.mvnd.client;

import javax.xml.stream.XMLStreamException;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
@@ -30,13 +28,11 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -45,8 +41,6 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.apache.maven.api.cli.extensions.CoreExtension;
import org.apache.maven.cling.internal.extension.io.CoreExtensionsStaxReader;
import org.mvndaemon.mvnd.common.Environment;
import org.mvndaemon.mvnd.common.InterpolationHelper;
import org.mvndaemon.mvnd.common.Os;
@@ -291,7 +285,7 @@ public String jdkJavaOpts() {

/**
* @return the number of threads (same syntax as Maven's {@code -T}/{@code --threads} option) to pass to the daemon
* unless the user passes his own `-T` or `--threads`.
* unless the user passes his own `-T` or `--threads`.
*/
public String threads() {
return property(Environment.MVND_THREADS)
@@ -321,7 +315,7 @@ public Path file() {

/**
* @return absolute normalized path to local Maven repository or {@code null} if the server is supposed to use the
* default
* default
*/
public Path mavenRepoLocal() {
return property(Environment.MAVEN_REPO_LOCAL).asPath();
@@ -346,16 +340,15 @@ public boolean debug() {
}

/**
*
* @return if mvnd should behave as maven
*/
public boolean serial() {
return value(Environment.SERIAL).orSystemProperty().orDefault().asBoolean();
}

/**
* @param newUserDir where to change the current directory to
* @return a new {@link DaemonParameters} with {@code userDir} set to the given {@code newUserDir}
* @param newUserDir where to change the current directory to
* @return a new {@link DaemonParameters} with {@code userDir} set to the given {@code newUserDir}
*/
public DaemonParameters cd(Path newUserDir) {
return derive(b -> b.put(Environment.USER_DIR, newUserDir));
@@ -457,14 +450,20 @@ private String defaultValue(Environment env) {
if (env == Environment.MVND_EXT_CLASSPATH) {
List<String> cp = parseExtClasspath(userHome());
return String.join(",", cp);
} else if (env == Environment.MVND_CORE_EXTENSIONS) {
} else if (env == Environment.MVND_CORE_EXTENSIONS_FILE_PATH) {
try {
List<String> extensions = readCoreExtensionsDescriptor(multiModuleProjectDirectory()).stream()
.map(e -> e.getGroupId() + ":" + e.getArtifactId() + ":" + e.getVersion())
.collect(Collectors.toList());
return String.join(";", extensions);
} catch (IOException | XMLStreamException e) {
throw new RuntimeException("Unable to parse core extensions", e);
return resolveCoreExtensionFilePath(multiModuleProjectDirectory());
} catch (IOException e) {
throw new RuntimeException("Unable to resolve core extension configuration file path", e);
}
} else if (env == Environment.MVND_CORE_EXTENSIONS_EXCLUDE) {
String exclusionsString = systemProperty(Environment.MVND_CORE_EXTENSIONS_EXCLUDE)
.orDefault()
.asString();
if (exclusionsString != null) {
return exclusionsString;
} else {
return "";
}
} else {
return env.getDefault();
@@ -483,40 +482,15 @@ private static List<String> parseExtClasspath(Path userDir) {
return jars;
}

private static List<CoreExtension> readCoreExtensionsDescriptor(Path multiModuleProjectDirectory)
throws IOException, XMLStreamException {
private static String resolveCoreExtensionFilePath(Path multiModuleProjectDirectory) throws IOException {
if (multiModuleProjectDirectory == null) {
return Collections.emptyList();
return "";
}
Path extensionsFile = multiModuleProjectDirectory.resolve(EXTENSIONS_FILENAME);
if (!Files.exists(extensionsFile)) {
return Collections.emptyList();
}
CoreExtensionsStaxReader parser = new CoreExtensionsStaxReader();
List<CoreExtension> extensions;
try (InputStream is = Files.newInputStream(extensionsFile)) {
extensions = parser.read(is).getExtensions();
}
return filterCoreExtensions(extensions);
}

private static List<CoreExtension> filterCoreExtensions(List<CoreExtension> coreExtensions) {
Set<String> exclusions = new HashSet<>();
String exclusionsString = systemProperty(Environment.MVND_CORE_EXTENSIONS_EXCLUDE)
.orDefault()
.asString();
if (exclusionsString != null) {
exclusions.addAll(Arrays.stream(exclusionsString.split(","))
.filter(e -> e != null && !e.trim().isEmpty())
.collect(Collectors.toList()));
}
if (!exclusions.isEmpty()) {
return coreExtensions.stream()
.filter(e -> !exclusions.contains(e.getGroupId() + ":" + e.getArtifactId()))
.collect(Collectors.toList());
} else {
return coreExtensions;
return "";
}
return extensionsFile.toUri().getPath();
}

private static Properties loadProperties(Path path) {
@@ -571,7 +545,9 @@ public ValueSource(Function<StringBuilder, StringBuilder> descriptionFunction, S
this.valueSupplier = valueSupplier;
}

/** Mostly for debugging */
/**
* Mostly for debugging
*/
@Override
public String toString() {
return descriptionFunction.apply(new StringBuilder()).toString();
Original file line number Diff line number Diff line change
@@ -212,9 +212,10 @@ public enum Environment {
*/
MVND_EXT_CLASSPATH("mvnd.extClasspath", null, null, OptionType.STRING, Flags.DISCRIMINATING | Flags.INTERNAL),
/**
* Internal option to specify the list of maven extension to register.
* Internal option to specify the maven extension configuration file path to register.
*/
MVND_CORE_EXTENSIONS("mvnd.coreExtensions", null, null, OptionType.STRING, Flags.DISCRIMINATING | Flags.INTERNAL),
MVND_CORE_EXTENSIONS_FILE_PATH(
"mvnd.coreExtensionFilePath", null, null, OptionType.STRING, Flags.DISCRIMINATING | Flags.INTERNAL),
/**
* Internal option to specify comma separated list of maven extension G:As to exclude (to not load them from
* .mvn/extensions.xml). This option makes possible for example that a project that with vanilla Maven would
@@ -226,7 +227,7 @@ public enum Environment {
null,
"io.takari.maven:takari-smart-builder",
OptionType.STRING,
Flags.OPTIONAL),
Flags.OPTIONAL | Flags.DISCRIMINATING),
/**
* The <code>-Xms</code> value to pass to the daemon.
* This option takes precedence over options specified in <code>-Dmvnd.jvmArgs</code>.
58 changes: 46 additions & 12 deletions daemon/src/main/java/org/apache/maven/cli/DaemonMavenParser.java
Original file line number Diff line number Diff line change
@@ -18,15 +18,25 @@
*/
package org.apache.maven.cli;

import javax.xml.stream.XMLStreamException;

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
import java.util.Set;
import java.util.stream.Collectors;

import org.apache.commons.cli.ParseException;
import org.apache.maven.api.cli.ParserException;
import org.apache.maven.api.cli.extensions.CoreExtension;
import org.apache.maven.api.cli.mvn.MavenOptions;
import org.apache.maven.cling.internal.extension.io.CoreExtensionsStaxReader;
import org.apache.maven.cling.invoker.mvn.MavenParser;
import org.mvndaemon.mvnd.common.Environment;

@@ -53,16 +63,40 @@ protected Map<String, String> populateSystemProperties(LocalContext context) thr

@Override
protected List<CoreExtension> readCoreExtensionsDescriptor(LocalContext context) {
return Stream.of(Environment.MVND_CORE_EXTENSIONS.asString().split(";"))
.filter(s -> s != null && !s.isEmpty())
.map(s -> {
String[] parts = s.split(":");
return CoreExtension.newBuilder()
.groupId(parts[0])
.artifactId(parts[1])
.version(parts[2])
.build();
})
.toList();
String path = Environment.MVND_CORE_EXTENSIONS_FILE_PATH.asString();
if (!path.isEmpty()) {
try {
return readCoreExtensionsDescriptor(Path.of(path));
} catch (Exception e) {
throw new RuntimeException(e);
}
} else {
return new ArrayList<>();
}
}

private List<CoreExtension> readCoreExtensionsDescriptor(Path extensionsFile)
throws IOException, XMLStreamException {

CoreExtensionsStaxReader parser = new CoreExtensionsStaxReader();
List<CoreExtension> extensions;
try (InputStream is = Files.newInputStream(extensionsFile)) {
extensions = parser.read(is).getExtensions();
}
return filterCoreExtensions(extensions);
}

private static List<CoreExtension> filterCoreExtensions(List<CoreExtension> coreExtensions) {
String exclusionsString = Environment.MVND_CORE_EXTENSIONS_EXCLUDE.asString();
Set<String> exclusions = Arrays.stream(exclusionsString.split(","))
.filter(e -> e != null && !e.trim().isEmpty())
.collect(Collectors.toSet());
if (!exclusions.isEmpty()) {
return coreExtensions.stream()
.filter(e -> !exclusions.contains(e.getGroupId() + ":" + e.getArtifactId()))
.collect(Collectors.toList());
} else {
return coreExtensions;
}
}
}
2 changes: 1 addition & 1 deletion dist/src/main/distro/conf/mvnd.properties
Original file line number Diff line number Diff line change
@@ -35,7 +35,7 @@
# a system property on the command line
# - mvnd.id: this property is used internally to identify the daemon being created
# - mvnd.extClasspath: internal option to specify the maven extension classpath
# - mvnd.coreExtensions: internal option to specify the list of maven extension to register
# - mvnd.coreExtensionFilePath: internal option to specify the maven extension configuration file path to register
#

# MVND_NO_BUFFERING
Original file line number Diff line number Diff line change
@@ -54,7 +54,6 @@ void version() throws IOException, InterruptedException {
client.execute(o, "-v").assertSuccess();
assertDaemonRegistrySize(1);
DaemonInfo daemon = registry.getAll().iterator().next();
assertTrue(daemon.getOptions().contains("mvnd.coreExtensions=fr.jcgay.maven:maven-profiler:3.0"));

registry.awaitIdle(daemon.getId());

@@ -67,4 +66,31 @@ private void assertDaemonRegistrySize(int size) {
.as("Daemon registry size should be " + size)
.isEqualTo(size);
}

@Test
void coreExtensionFilePathOption() throws InterruptedException {
registry.killAll();
assertDaemonRegistrySize(0);

final TestClientOutput o = new TestClientOutput();
client.execute(o, "-v").assertSuccess();
assertDaemonRegistrySize(1);
DaemonInfo daemon = registry.getAll().iterator().next();
daemon.getOptions().stream()
.filter(s -> s.startsWith("mvnd.coreExtensionFilePath="))
.findFirst()
.ifPresent(s -> assertTrue(s.endsWith(".mvn/extensions.xml")));
}

@Test
void coreExtensionExcludeOption() throws InterruptedException {
registry.killAll();
assertDaemonRegistrySize(0);

final TestClientOutput o = new TestClientOutput();
client.execute(o, "-v").assertSuccess();
assertDaemonRegistrySize(1);
DaemonInfo daemon = registry.getAll().iterator().next();
assertTrue(daemon.getOptions().contains("mvnd.coreExtensionsExclude=io.takari.maven:takari-smart-builder"));
}
}

0 comments on commit 6858c52

Please sign in to comment.