Bug: extension handling (#1282)

mvnd still had mvn3 like code, just drop it and let base parser
do the work. All the mvnd needs is filtering.

Discriminating based on XML file hashes.

Fixes #1280
This commit is contained in:
Tamas Cservenak
2025-03-07 19:02:57 +01:00
committed by GitHub
parent bb1191e4c6
commit c4b5656d19
3 changed files with 53 additions and 53 deletions

View File

@@ -21,16 +21,20 @@ package org.mvndaemon.mvnd.client;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.security.MessageDigest;
import java.time.Duration; import java.time.Duration;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HexFormat;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.Properties; import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@@ -450,21 +454,13 @@ public class DaemonParameters {
if (env == Environment.MVND_EXT_CLASSPATH) { if (env == Environment.MVND_EXT_CLASSPATH) {
List<String> cp = parseExtClasspath(userHome()); List<String> cp = parseExtClasspath(userHome());
return String.join(",", cp); return String.join(",", cp);
} else if (env == Environment.MVND_CORE_EXTENSIONS_FILE_PATH) { } else if (env == Environment.MVND_CORE_EXTENSIONS_DISCRIMINATOR) {
try { return calculateCoreExtensionsDiscriminator(multiModuleProjectDirectory(), userHome(), mvndHome());
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) { } else if (env == Environment.MVND_CORE_EXTENSIONS_EXCLUDE) {
String exclusionsString = systemProperty(Environment.MVND_CORE_EXTENSIONS_EXCLUDE) String exclusionsString = systemProperty(Environment.MVND_CORE_EXTENSIONS_EXCLUDE)
.orDefault() .orDefault()
.asString(); .asString();
if (exclusionsString != null) { return Objects.requireNonNullElse(exclusionsString, "");
return exclusionsString;
} else {
return "";
}
} else { } else {
return env.getDefault(); return env.getDefault();
} }
@@ -482,15 +478,43 @@ public class DaemonParameters {
return jars; return jars;
} }
private static String resolveCoreExtensionFilePath(Path multiModuleProjectDirectory) throws IOException { private static String calculateCoreExtensionsDiscriminator(
if (multiModuleProjectDirectory == null) { Path multiModuleProjectDirectory, Path userHome, Path mvndHome) {
return ""; try {
Path projectExtensionsXml = multiModuleProjectDirectory
.resolve(".mvn")
.resolve("extensions.xml")
.toAbsolutePath()
.normalize();
Path userExtensionsXml = userHome.resolve(".m2")
.resolve("extensions.xml")
.toAbsolutePath()
.normalize();
Path installationExtensionsXml = mvndHome.resolve("mvn")
.resolve("conf")
.resolve("extensions.xml")
.toAbsolutePath()
.normalize();
String blob = "";
if (Files.exists(projectExtensionsXml)) {
blob += projectExtensionsXml.toString();
blob += Files.readString(projectExtensionsXml);
} }
Path extensionsFile = multiModuleProjectDirectory.resolve(EXTENSIONS_FILENAME); if (Files.exists(userExtensionsXml)) {
if (!Files.exists(extensionsFile)) { blob += userExtensionsXml.toString();
return ""; blob += Files.readString(userExtensionsXml);
}
if (Files.exists(installationExtensionsXml)) {
blob += installationExtensionsXml.toString();
blob += Files.readString(installationExtensionsXml);
}
MessageDigest digest = MessageDigest.getInstance("SHA-1");
digest.update(blob.getBytes(StandardCharsets.UTF_8));
return HexFormat.of().formatHex(digest.digest());
} catch (Exception e) {
throw new IllegalStateException("Cannot calculate core extensions discriminator", e);
} }
return extensionsFile.toAbsolutePath().toString();
} }
private static Properties loadProperties(Path path) { private static Properties loadProperties(Path path) {

View File

@@ -212,10 +212,13 @@ public enum Environment {
*/ */
MVND_EXT_CLASSPATH("mvnd.extClasspath", null, null, OptionType.STRING, Flags.DISCRIMINATING | Flags.INTERNAL), MVND_EXT_CLASSPATH("mvnd.extClasspath", null, null, OptionType.STRING, Flags.DISCRIMINATING | Flags.INTERNAL),
/** /**
* Internal option to specify the maven extension configuration file path to register. * Internal option to hold the maven extension configuration files "discriminator" to discriminate among daemons.
* Value of this option is irrelevant and is unused on daemon side. The only purpose and requirement is that the value
* discriminate the daemon based on 3 extension file contents (project, user, installation), and if any file change,
* this option value should change as well. It should behave like a hash value calculated out of 3 file contents would.
*/ */
MVND_CORE_EXTENSIONS_FILE_PATH( MVND_CORE_EXTENSIONS_DISCRIMINATOR(
"mvnd.coreExtensionFilePath", null, null, OptionType.STRING, Flags.DISCRIMINATING | Flags.INTERNAL), "mvnd.coreExtensionsDiscriminator", 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 * 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 * .mvn/extensions.xml). This option makes possible for example that a project that with vanilla Maven would

View File

@@ -18,13 +18,7 @@
*/ */
package org.apache.maven.cli; 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.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@@ -35,7 +29,6 @@ import java.util.stream.Collectors;
import org.apache.commons.cli.ParseException; import org.apache.commons.cli.ParseException;
import org.apache.maven.api.cli.extensions.CoreExtension; import org.apache.maven.api.cli.extensions.CoreExtension;
import org.apache.maven.api.cli.mvn.MavenOptions; 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.apache.maven.cling.invoker.mvn.MavenParser;
import org.mvndaemon.mvnd.common.Environment; import org.mvndaemon.mvnd.common.Environment;
@@ -57,34 +50,14 @@ public class DaemonMavenParser extends MavenParser {
} }
@Override @Override
protected List<CoreExtension> readCoreExtensionsDescriptor(LocalContext context) { protected List<CoreExtension> readCoreExtensionsDescriptorFromFile(Path extensionsFile) {
String coreExtensionsFilePath = Environment.MVND_CORE_EXTENSIONS_FILE_PATH.asString(); return filterCoreExtensions(super.readCoreExtensionsDescriptorFromFile(extensionsFile));
if (!coreExtensionsFilePath.isEmpty()) {
try {
return readCoreExtensionsDescriptor(Path.of(coreExtensionsFilePath));
} catch (Exception e) {
throw new RuntimeException(e);
}
} else {
return new ArrayList<>();
}
} }
private List<CoreExtension> readCoreExtensionsDescriptor(Path extensionsFile) protected static List<CoreExtension> filterCoreExtensions(List<CoreExtension> coreExtensions) {
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(); String exclusionsString = Environment.MVND_CORE_EXTENSIONS_EXCLUDE.asString();
Set<String> exclusions = Arrays.stream(exclusionsString.split(",")) Set<String> exclusions = Arrays.stream(exclusionsString.split(","))
.filter(e -> e != null && !e.trim().isEmpty()) .filter(e -> !e.trim().isEmpty())
.collect(Collectors.toSet()); .collect(Collectors.toSet());
if (!exclusions.isEmpty()) { if (!exclusions.isEmpty()) {
return coreExtensions.stream() return coreExtensions.stream()