diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/Constants.java b/api/maven-api-core/src/main/java/org/apache/maven/api/Constants.java index df109e3ac4a4..db96d3f7f610 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/Constants.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/Constants.java @@ -526,6 +526,16 @@ public final class Constants { public static final String MAVEN_VERSION_RANGE_RESOLVER_NATURE_OVERRIDE = "maven.versionRangeResolver.natureOverride"; + /** + * Comma-separated list of XML contexts/fields to intern during POM parsing for memory optimization. + * When not specified, a default set of commonly repeated contexts will be used. + * Example: "groupId,artifactId,version,scope,type" + * + * @since 4.0.0 + */ + @Config + public static final String MAVEN_MODEL_BUILDER_INTERNS = "maven.modelBuilder.interns"; + /** * All system properties used by Maven Logger start with this prefix. * diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultModelXmlFactory.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultModelXmlFactory.java index e7c9cf884bfa..f447627cc794 100644 --- a/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultModelXmlFactory.java +++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultModelXmlFactory.java @@ -92,7 +92,9 @@ private Model doRead(XmlReaderRequest request) throws XmlReaderException { source = new InputSource( request.getModelId(), path != null ? path.toUri().toString() : null); } - MavenStaxReader xml = new MavenStaxReader(); + MavenStaxReader xml = request.getTransformer() != null + ? new MavenStaxReader(request.getTransformer()::transform) + : new MavenStaxReader(); xml.setAddDefaultEntities(request.isAddDefaultEntities()); if (inputStream != null) { return xml.read(inputStream, request.isStrict(), source); diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultPluginXmlFactory.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultPluginXmlFactory.java index f6dc37400e97..c287a7d91422 100644 --- a/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultPluginXmlFactory.java +++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultPluginXmlFactory.java @@ -56,7 +56,9 @@ public PluginDescriptor read(@Nonnull XmlReaderRequest request) throws XmlReader throw new IllegalArgumentException("path, url, reader or inputStream must be non null"); } try { - PluginDescriptorStaxReader xml = new PluginDescriptorStaxReader(); + PluginDescriptorStaxReader xml = request.getTransformer() != null + ? new PluginDescriptorStaxReader(request.getTransformer()::transform) + : new PluginDescriptorStaxReader(); xml.setAddDefaultEntities(request.isAddDefaultEntities()); if (inputStream != null) { return xml.read(inputStream, request.isStrict()); diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultSettingsXmlFactory.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultSettingsXmlFactory.java index fd1749cd0e06..348c8a9a8b85 100644 --- a/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultSettingsXmlFactory.java +++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultSettingsXmlFactory.java @@ -55,7 +55,9 @@ public Settings read(@Nonnull XmlReaderRequest request) throws XmlReaderExceptio if (request.getModelId() != null || request.getLocation() != null) { source = new InputSource(request.getLocation()); } - SettingsStaxReader xml = new SettingsStaxReader(); + SettingsStaxReader xml = request.getTransformer() != null + ? new SettingsStaxReader(request.getTransformer()::transform) + : new SettingsStaxReader(); xml.setAddDefaultEntities(request.isAddDefaultEntities()); if (reader != null) { return xml.read(reader, request.isStrict(), source); diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultToolchainsXmlFactory.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultToolchainsXmlFactory.java index 2db24aa8ec0f..18a6bd836859 100644 --- a/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultToolchainsXmlFactory.java +++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultToolchainsXmlFactory.java @@ -57,7 +57,9 @@ public PersistedToolchains read(@Nonnull XmlReaderRequest request) throws XmlRea if (request.getModelId() != null || request.getLocation() != null) { source = new InputSource(request.getLocation()); } - MavenToolchainsStaxReader xml = new MavenToolchainsStaxReader(); + MavenToolchainsStaxReader xml = request.getTransformer() != null + ? new MavenToolchainsStaxReader(request.getTransformer()::transform) + : new MavenToolchainsStaxReader(); xml.setAddDefaultEntities(request.isAddDefaultEntities()); if (reader != null) { return xml.read(reader, request.isStrict(), source); diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelBuilder.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelBuilder.java index e4c0d3bdd8a0..5a5735a7922e 100644 --- a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelBuilder.java +++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelBuilder.java @@ -26,6 +26,7 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; @@ -1254,7 +1255,7 @@ Model doReadFileModel() throws ModelBuilderException { .path(modelSource.getPath()) .rootDirectory(rootDirectory) .inputStream(is) - .transformer(new InliningTransformer()) + .transformer(new InterningTransformer(session)) .build()); } catch (XmlReaderException e) { if (!strict) { @@ -1267,7 +1268,7 @@ Model doReadFileModel() throws ModelBuilderException { .path(modelSource.getPath()) .rootDirectory(rootDirectory) .inputStream(is) - .transformer(new InliningTransformer()) + .transformer(new InterningTransformer(session)) .build()); } catch (XmlReaderException ne) { // still unreadable even in non-strict mode, rethrow original error @@ -2144,23 +2145,94 @@ public R getRequest() { } } - static class InliningTransformer implements XmlReaderRequest.Transformer { - static final Set CONTEXTS = Set.of( + static class InterningTransformer implements XmlReaderRequest.Transformer { + static final Set DEFAULT_CONTEXTS = Set.of( + // Core Maven coordinates "groupId", "artifactId", "version", "namespaceUri", "packaging", + + // Dependency-related fields "scope", + "type", + "classifier", + + // Build and plugin-related fields "phase", + "goal", + "execution", + + // Repository-related fields "layout", "policy", "checksumPolicy", - "updatePolicy"); + "updatePolicy", + + // Common metadata fields + "modelVersion", + "name", + "url", + "system", + "distribution", + "status", + + // SCM fields + "connection", + "developerConnection", + "tag", + + // Common enum-like values that appear frequently + "id", + "inherited", + "optional"); + + private final Set contexts; + + /** + * Creates an InterningTransformer with default contexts. + */ + InterningTransformer() { + this.contexts = DEFAULT_CONTEXTS; + } + + /** + * Creates an InterningTransformer with contexts from session properties. + * + * @param session the Maven session to read properties from + */ + InterningTransformer(Session session) { + this.contexts = parseContextsFromSession(session); + } + + private Set parseContextsFromSession(Session session) { + String contextsProperty = session.getUserProperties().get(Constants.MAVEN_MODEL_BUILDER_INTERNS); + if (contextsProperty == null) { + contextsProperty = session.getSystemProperties().get(Constants.MAVEN_MODEL_BUILDER_INTERNS); + } + + if (contextsProperty == null || contextsProperty.trim().isEmpty()) { + return DEFAULT_CONTEXTS; + } + + return Arrays.stream(contextsProperty.split(",")) + .map(String::trim) + .filter(s -> !s.isEmpty()) + .collect(Collectors.toSet()); + } @Override public String transform(String input, String context) { - return CONTEXTS.contains(context) ? input.intern() : input; + return input != null && contexts.contains(context) ? input.intern() : input; + } + + /** + * Get the contexts that will be interned by this transformer. + * Used for testing purposes. + */ + Set getContexts() { + return contexts; } } } diff --git a/impl/maven-impl/src/test/java/org/apache/maven/impl/XmlFactoryTransformerTest.java b/impl/maven-impl/src/test/java/org/apache/maven/impl/XmlFactoryTransformerTest.java new file mode 100644 index 000000000000..aa1d3e152219 --- /dev/null +++ b/impl/maven-impl/src/test/java/org/apache/maven/impl/XmlFactoryTransformerTest.java @@ -0,0 +1,246 @@ +/* + * 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.impl; + +import java.io.StringReader; +import java.util.ArrayList; +import java.util.List; + +import org.apache.maven.api.model.Model; +import org.apache.maven.api.plugin.descriptor.PluginDescriptor; +import org.apache.maven.api.services.xml.XmlReaderRequest; +import org.apache.maven.api.settings.Settings; +import org.apache.maven.api.toolchain.PersistedToolchains; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Test that all XML factories properly use the transformer from XmlReaderRequest. + */ +class XmlFactoryTransformerTest { + + @Test + void testModelXmlFactoryUsesTransformer() throws Exception { + // Create a test transformer that tracks what contexts are called + List calledContexts = new ArrayList<>(); + XmlReaderRequest.Transformer trackingTransformer = (value, context) -> { + calledContexts.add(context); + return value; + }; + + String pomXml = + """ + + + 4.0.0 + com.example + test-project + 1.0.0 + jar + + """; + + DefaultModelXmlFactory factory = new DefaultModelXmlFactory(); + XmlReaderRequest request = XmlReaderRequest.builder() + .reader(new StringReader(pomXml)) + .transformer(trackingTransformer) + .build(); + + Model model = factory.read(request); + + // Verify the model was parsed correctly + assertEquals("com.example", model.getGroupId()); + assertEquals("test-project", model.getArtifactId()); + assertEquals("1.0.0", model.getVersion()); + assertEquals("jar", model.getPackaging()); + + // Verify that the transformer was called + assertFalse(calledContexts.isEmpty(), "Transformer should have been called"); + assertTrue(calledContexts.contains("groupId"), "groupId context should be called"); + assertTrue(calledContexts.contains("artifactId"), "artifactId context should be called"); + assertTrue(calledContexts.contains("version"), "version context should be called"); + assertTrue(calledContexts.contains("packaging"), "packaging context should be called"); + } + + @Test + void testSettingsXmlFactoryUsesTransformer() throws Exception { + // Create a test transformer that tracks what contexts are called + List calledContexts = new ArrayList<>(); + XmlReaderRequest.Transformer trackingTransformer = (value, context) -> { + calledContexts.add(context); + return value; + }; + + String settingsXml = + """ + + + /path/to/local/repo + + + test-server + testuser + testpass + + + + """; + + DefaultSettingsXmlFactory factory = new DefaultSettingsXmlFactory(); + XmlReaderRequest request = XmlReaderRequest.builder() + .reader(new StringReader(settingsXml)) + .transformer(trackingTransformer) + .build(); + + Settings settings = factory.read(request); + + // Verify the settings were parsed correctly + assertEquals("/path/to/local/repo", settings.getLocalRepository()); + assertEquals(1, settings.getServers().size()); + assertEquals("test-server", settings.getServers().get(0).getId()); + assertEquals("testuser", settings.getServers().get(0).getUsername()); + assertEquals("testpass", settings.getServers().get(0).getPassword()); + + // Verify that the transformer was called + assertFalse(calledContexts.isEmpty(), "Transformer should have been called"); + assertTrue(calledContexts.contains("localRepository"), "localRepository context should be called"); + assertTrue(calledContexts.contains("id"), "id context should be called"); + assertTrue(calledContexts.contains("username"), "username context should be called"); + assertTrue(calledContexts.contains("password"), "password context should be called"); + } + + @Test + void testToolchainsXmlFactoryUsesTransformer() throws Exception { + // Create a test transformer that tracks what contexts are called + List calledContexts = new ArrayList<>(); + XmlReaderRequest.Transformer trackingTransformer = (value, context) -> { + calledContexts.add(context); + return value; + }; + + String toolchainsXml = + """ + + + + jdk + + 17 + openjdk + + + /path/to/jdk17 + + + + """; + + DefaultToolchainsXmlFactory factory = new DefaultToolchainsXmlFactory(); + XmlReaderRequest request = XmlReaderRequest.builder() + .reader(new StringReader(toolchainsXml)) + .transformer(trackingTransformer) + .build(); + + PersistedToolchains toolchains = factory.read(request); + + // Verify the toolchains were parsed correctly + assertEquals(1, toolchains.getToolchains().size()); + assertEquals("jdk", toolchains.getToolchains().get(0).getType()); + assertEquals("17", toolchains.getToolchains().get(0).getProvides().get("version")); + assertEquals("openjdk", toolchains.getToolchains().get(0).getProvides().get("vendor")); + assertEquals( + "/path/to/jdk17", + toolchains + .getToolchains() + .get(0) + .getConfiguration() + .child("jdkHome") + .value()); + + // Verify that the transformer was called + assertFalse(calledContexts.isEmpty(), "Transformer should have been called"); + assertTrue(calledContexts.contains("type"), "type context should be called"); + + // Note: The provides and configuration sections are parsed as Maps/DOM, + // so individual elements like "version", "vendor", "jdkHome" may not + // trigger the transformer directly. The important thing is that the + // transformer is being used by the factory. + } + + @Test + void testPluginXmlFactoryUsesTransformer() throws Exception { + // Create a test transformer that tracks what contexts are called + List calledContexts = new ArrayList<>(); + XmlReaderRequest.Transformer trackingTransformer = (value, context) -> { + calledContexts.add(context); + return value; + }; + + String pluginXml = + """ + + + test-plugin + com.example + test-maven-plugin + 1.0.0 + test + + + compile + compile + com.example.TestMojo + + + + """; + + DefaultPluginXmlFactory factory = new DefaultPluginXmlFactory(); + XmlReaderRequest request = XmlReaderRequest.builder() + .reader(new StringReader(pluginXml)) + .transformer(trackingTransformer) + .build(); + + PluginDescriptor plugin = factory.read(request); + + // Verify the plugin was parsed correctly + assertEquals("test-plugin", plugin.getName()); + assertEquals("com.example", plugin.getGroupId()); + assertEquals("test-maven-plugin", plugin.getArtifactId()); + assertEquals("1.0.0", plugin.getVersion()); + assertEquals("test", plugin.getGoalPrefix()); + assertEquals(1, plugin.getMojos().size()); + assertEquals("compile", plugin.getMojos().get(0).getGoal()); + assertEquals("compile", plugin.getMojos().get(0).getPhase()); + assertEquals("com.example.TestMojo", plugin.getMojos().get(0).getImplementation()); + + // Verify that the transformer was called + assertFalse(calledContexts.isEmpty(), "Transformer should have been called"); + assertTrue(calledContexts.contains("name"), "name context should be called"); + assertTrue(calledContexts.contains("groupId"), "groupId context should be called"); + assertTrue(calledContexts.contains("artifactId"), "artifactId context should be called"); + assertTrue(calledContexts.contains("version"), "version context should be called"); + assertTrue(calledContexts.contains("goal"), "goal context should be called"); + assertTrue(calledContexts.contains("phase"), "phase context should be called"); + assertTrue(calledContexts.contains("implementation"), "implementation context should be called"); + } +} diff --git a/impl/maven-impl/src/test/java/org/apache/maven/impl/model/InterningTransformerTest.java b/impl/maven-impl/src/test/java/org/apache/maven/impl/model/InterningTransformerTest.java new file mode 100644 index 000000000000..03e8f9018cc4 --- /dev/null +++ b/impl/maven-impl/src/test/java/org/apache/maven/impl/model/InterningTransformerTest.java @@ -0,0 +1,413 @@ +/* + * 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.impl.model; + +import java.io.StringReader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.maven.api.Constants; +import org.apache.maven.api.Session; +import org.apache.maven.api.model.Model; +import org.apache.maven.api.services.xml.XmlReaderRequest; +import org.apache.maven.impl.DefaultModelXmlFactory; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.when; + +/** + * Test class for {@link DefaultModelBuilder.InterningTransformer}. + * Verifies that the transformer correctly interns commonly used string values + * to reduce memory usage during Maven POM parsing. + */ +class InterningTransformerTest { + + @Test + void testTransformerInternsCorrectContexts() { + DefaultModelBuilder.InterningTransformer transformer = new DefaultModelBuilder.InterningTransformer(); + + // Test that contexts in the CONTEXTS set are interned + String groupId1 = transformer.transform("org.apache.maven", "groupId"); + String groupId2 = transformer.transform("org.apache.maven", "groupId"); + assertSame(groupId1, groupId2, "groupId should be interned"); + + String type1 = transformer.transform("jar", "type"); + String type2 = transformer.transform("jar", "type"); + assertSame(type1, type2, "type should be interned"); + + String scope1 = transformer.transform("compile", "scope"); + String scope2 = transformer.transform("compile", "scope"); + assertSame(scope1, scope2, "scope should be interned"); + + String classifier1 = transformer.transform("sources", "classifier"); + String classifier2 = transformer.transform("sources", "classifier"); + assertSame(classifier1, classifier2, "classifier should be interned"); + + String goal1 = transformer.transform("compile", "goal"); + String goal2 = transformer.transform("compile", "goal"); + assertSame(goal1, goal2, "goal should be interned"); + + String modelVersion1 = transformer.transform("4.0.0", "modelVersion"); + String modelVersion2 = transformer.transform("4.0.0", "modelVersion"); + assertSame(modelVersion1, modelVersion2, "modelVersion should be interned"); + + // Test that contexts not in the CONTEXTS set are not interned + // Use new String() to avoid automatic interning by JVM + String value1 = new String("some-value"); + String value2 = new String("some-value"); + String nonInterned1 = transformer.transform(value1, "nonInterned"); + String nonInterned2 = transformer.transform(value2, "nonInterned"); + assertSame(value1, nonInterned1, "non-interned context should return same instance"); + assertSame(value2, nonInterned2, "non-interned context should return same instance"); + assertNotSame(nonInterned1, nonInterned2, "different input instances should remain different"); + assertEquals(nonInterned1, nonInterned2, "but values should still be equal"); + } + + @Test + void testTransformerContainsExpectedContexts() { + // Verify that the DEFAULT_CONTEXTS set contains all the expected fields + assertTrue(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("groupId")); + assertTrue(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("artifactId")); + assertTrue(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("version")); + assertTrue(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("packaging")); + assertTrue(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("scope")); + assertTrue(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("type")); + assertTrue(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("classifier")); + assertTrue(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("goal")); + assertTrue(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("execution")); + assertTrue(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("phase")); + assertTrue(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("modelVersion")); + assertTrue(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("name")); + assertTrue(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("url")); + assertTrue(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("system")); + assertTrue(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("distribution")); + assertTrue(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("status")); + assertTrue(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("connection")); + assertTrue(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("developerConnection")); + assertTrue(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("tag")); + assertTrue(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("id")); + assertTrue(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("inherited")); + assertTrue(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("optional")); + + // Verify that non-interned contexts are not in the set + assertFalse(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("nonInterned")); + assertFalse(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("description")); + assertFalse(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("randomField")); + } + + @Test + void testTransformerWithNullAndEmptyValues() { + DefaultModelBuilder.InterningTransformer transformer = new DefaultModelBuilder.InterningTransformer(); + + // Test with null value + String result1 = transformer.transform(null, "groupId"); + String result2 = transformer.transform(null, "groupId"); + assertNull(result1); + assertNull(result2); + + // Test with empty string + String empty1 = transformer.transform("", "artifactId"); + String empty2 = transformer.transform("", "artifactId"); + assertSame(empty1, empty2, "empty strings should be interned"); + + // Test with whitespace + String whitespace1 = transformer.transform(" ", "version"); + String whitespace2 = transformer.transform(" ", "version"); + assertSame(whitespace1, whitespace2, "whitespace strings should be interned"); + } + + @Test + void testTransformerIsUsedDuringPomParsing() throws Exception { + // Create a test transformer that tracks what contexts are called + List calledContexts = new ArrayList<>(); + XmlReaderRequest.Transformer trackingTransformer = (value, context) -> { + calledContexts.add(context); + return value; + }; + + String pomXml = + """ + + + 4.0.0 + com.example + test-project + 1.0.0 + jar + + + + org.apache.maven + maven-core + 3.9.0 + compile + jar + sources + + + + """; + + DefaultModelXmlFactory factory = new DefaultModelXmlFactory(); + XmlReaderRequest request = XmlReaderRequest.builder() + .reader(new StringReader(pomXml)) + .transformer(trackingTransformer) + .build(); + + Model model = factory.read(request); + + // Verify the model was parsed correctly + assertEquals("com.example", model.getGroupId()); + assertEquals("test-project", model.getArtifactId()); + assertEquals("1.0.0", model.getVersion()); + assertEquals("jar", model.getPackaging()); + + // Verify that the transformer was called for the expected contexts + assertTrue(calledContexts.contains("groupId"), "groupId context should be called"); + assertTrue(calledContexts.contains("artifactId"), "artifactId context should be called"); + assertTrue(calledContexts.contains("version"), "version context should be called"); + assertTrue(calledContexts.contains("packaging"), "packaging context should be called"); + assertTrue(calledContexts.contains("scope"), "scope context should be called"); + assertTrue(calledContexts.contains("type"), "type context should be called"); + assertTrue(calledContexts.contains("classifier"), "classifier context should be called"); + + // Verify specific paths are called correctly + long groupIdCount = calledContexts.stream().filter("groupId"::equals).count(); + assertTrue(groupIdCount >= 2, "groupId should be called at least 2 times (project, dependency)"); + } + + @Test + void testInterningTransformerWithRealPomParsing() throws Exception { + String pomXml = + """ + + + 4.0.0 + org.apache.maven + maven-core + 4.0.0 + jar + + + + org.apache.maven + maven-api + 4.0.0 + compile + + + + """; + + DefaultModelXmlFactory factory = new DefaultModelXmlFactory(); + DefaultModelBuilder.InterningTransformer transformer = new DefaultModelBuilder.InterningTransformer(); + + XmlReaderRequest request = XmlReaderRequest.builder() + .reader(new StringReader(pomXml)) + .transformer(transformer) + .build(); + + Model model = factory.read(request); + + // Verify the model was parsed correctly + assertEquals("org.apache.maven", model.getGroupId()); + assertEquals("maven-core", model.getArtifactId()); + assertEquals("4.0.0", model.getVersion()); + assertEquals("jar", model.getPackaging()); + + // Verify dependency was parsed + assertEquals(1, model.getDependencies().size()); + assertEquals("org.apache.maven", model.getDependencies().get(0).getGroupId()); + assertEquals("maven-api", model.getDependencies().get(0).getArtifactId()); + assertEquals("4.0.0", model.getDependencies().get(0).getVersion()); + assertEquals("compile", model.getDependencies().get(0).getScope()); + } + + @Test + void testTransformerWithSessionPropertyUserProperties() { + // Test with custom contexts from user properties + Map userProperties = new HashMap<>(); + userProperties.put(Constants.MAVEN_MODEL_BUILDER_INTERNS, "groupId,artifactId,customField"); + + Session session = Mockito.mock(Session.class); + when(session.getUserProperties()).thenReturn(userProperties); + + DefaultModelBuilder.InterningTransformer transformer = new DefaultModelBuilder.InterningTransformer(session); + + // Test that custom contexts are used + assertTrue(transformer.getContexts().contains("groupId")); + assertTrue(transformer.getContexts().contains("artifactId")); + assertTrue(transformer.getContexts().contains("customField")); + + // Test that default contexts not in the custom list are not used + assertFalse(transformer.getContexts().contains("version")); + assertFalse(transformer.getContexts().contains("scope")); + + // Test interning behavior + String groupId1 = transformer.transform("org.apache.maven", "groupId"); + String groupId2 = transformer.transform("org.apache.maven", "groupId"); + assertSame(groupId1, groupId2, "groupId should be interned"); + + String custom1 = transformer.transform("test-value", "customField"); + String custom2 = transformer.transform("test-value", "customField"); + assertSame(custom1, custom2, "customField should be interned"); + + // Test that non-custom contexts are not interned + String version1 = new String("1.0.0"); + String version2 = new String("1.0.0"); + String nonInterned1 = transformer.transform(version1, "version"); + String nonInterned2 = transformer.transform(version2, "version"); + assertSame(version1, nonInterned1, "version should not be interned"); + assertSame(version2, nonInterned2, "version should not be interned"); + assertNotSame(nonInterned1, nonInterned2, "different input instances should remain different"); + } + + @Test + void testTransformerWithSessionPropertySystemProperties() { + // Test with custom contexts from system properties + Map systemProperties = new HashMap<>(); + systemProperties.put(Constants.MAVEN_MODEL_BUILDER_INTERNS, "scope,type"); + + Session session = Mockito.mock(Session.class); + when(session.getSystemProperties()).thenReturn(systemProperties); + + DefaultModelBuilder.InterningTransformer transformer = new DefaultModelBuilder.InterningTransformer(session); + + // Test that custom contexts are used + assertTrue(transformer.getContexts().contains("scope")); + assertTrue(transformer.getContexts().contains("type")); + assertEquals(2, transformer.getContexts().size()); + + // Test interning behavior + String scope1 = transformer.transform("compile", "scope"); + String scope2 = transformer.transform("compile", "scope"); + assertSame(scope1, scope2, "scope should be interned"); + } + + @Test + void testTransformerUserPropertiesOverrideSystemProperties() { + // Test that user properties take precedence over system properties + Map systemProperties = new HashMap<>(); + systemProperties.put(Constants.MAVEN_MODEL_BUILDER_INTERNS, "scope,type"); + + Map userProperties = new HashMap<>(); + userProperties.put(Constants.MAVEN_MODEL_BUILDER_INTERNS, "groupId,artifactId"); + + Session session = Mockito.mock(Session.class); + when(session.getUserProperties()).thenReturn(userProperties); + when(session.getSystemProperties()).thenReturn(systemProperties); + + DefaultModelBuilder.InterningTransformer transformer = new DefaultModelBuilder.InterningTransformer(session); + + // Test that user properties are used, not system properties + assertTrue(transformer.getContexts().contains("groupId")); + assertTrue(transformer.getContexts().contains("artifactId")); + assertFalse(transformer.getContexts().contains("scope")); + assertFalse(transformer.getContexts().contains("type")); + assertEquals(2, transformer.getContexts().size()); + } + + @Test + void testTransformerWithEmptySessionProperty() { + // Test with empty property value - should use defaults + Map userProperties = new HashMap<>(); + userProperties.put(Constants.MAVEN_MODEL_BUILDER_INTERNS, ""); + + Session session = Mockito.mock(Session.class); + when(session.getUserProperties()).thenReturn(userProperties); + + DefaultModelBuilder.InterningTransformer transformer = new DefaultModelBuilder.InterningTransformer(session); + + // Should use default contexts + assertEquals(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS, transformer.getContexts()); + } + + @Test + void testTransformerWithWhitespaceOnlySessionProperty() { + // Test with whitespace-only property value - should use defaults + Map userProperties = new HashMap<>(); + userProperties.put(Constants.MAVEN_MODEL_BUILDER_INTERNS, " "); + + Session session = Mockito.mock(Session.class); + when(session.getUserProperties()).thenReturn(userProperties); + + DefaultModelBuilder.InterningTransformer transformer = new DefaultModelBuilder.InterningTransformer(session); + + // Should use default contexts + assertEquals(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS, transformer.getContexts()); + } + + @Test + void testTransformerWithNoSessionProperty() { + // Test with no property set - should use defaults + Session session = Mockito.mock(Session.class); + + DefaultModelBuilder.InterningTransformer transformer = new DefaultModelBuilder.InterningTransformer(session); + + // Should use default contexts + assertEquals(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS, transformer.getContexts()); + } + + @Test + void testTransformerWithCommaSeparatedValues() { + // Test parsing of comma-separated values with various whitespace + Map userProperties = new HashMap<>(); + userProperties.put(Constants.MAVEN_MODEL_BUILDER_INTERNS, "groupId, artifactId , version, scope ,type"); + + Session session = Mockito.mock(Session.class); + when(session.getUserProperties()).thenReturn(userProperties); + + DefaultModelBuilder.InterningTransformer transformer = new DefaultModelBuilder.InterningTransformer(session); + + // Test that all values are parsed correctly (whitespace trimmed) + assertTrue(transformer.getContexts().contains("groupId")); + assertTrue(transformer.getContexts().contains("artifactId")); + assertTrue(transformer.getContexts().contains("version")); + assertTrue(transformer.getContexts().contains("scope")); + assertTrue(transformer.getContexts().contains("type")); + assertEquals(5, transformer.getContexts().size()); + } + + @Test + void testTransformerWithEmptyCommaSeparatedValues() { + // Test parsing with empty values in comma-separated list + Map userProperties = new HashMap<>(); + userProperties.put(Constants.MAVEN_MODEL_BUILDER_INTERNS, "groupId,,artifactId, ,version"); + + Session session = Mockito.mock(Session.class); + when(session.getUserProperties()).thenReturn(userProperties); + + DefaultModelBuilder.InterningTransformer transformer = new DefaultModelBuilder.InterningTransformer(session); + + // Test that empty values are filtered out + assertTrue(transformer.getContexts().contains("groupId")); + assertTrue(transformer.getContexts().contains("artifactId")); + assertTrue(transformer.getContexts().contains("version")); + assertEquals(3, transformer.getContexts().size()); + } +} diff --git a/src/site/markdown/configuration.properties b/src/site/markdown/configuration.properties index c02842a07806..d0b3d51d86c1 100644 --- a/src/site/markdown/configuration.properties +++ b/src/site/markdown/configuration.properties @@ -20,7 +20,7 @@ # Generated from: maven-resolver-tools/src/main/resources/configuration.properties.vm # To modify this file, edit the template and regenerate. # -props.count = 64 +props.count = 65 props.1.key = maven.build.timestamp.format props.1.configurationType = String props.1.description = Build timestamp format. @@ -158,248 +158,254 @@ props.23.description = User property for controlling "maven personality". If act props.23.defaultValue = false props.23.since = 4.0.0 props.23.configurationSource = User properties -props.24.key = maven.modelBuilder.parallelism -props.24.configurationType = Integer -props.24.description = ProjectBuilder parallelism. -props.24.defaultValue = cores/2 + 1 +props.24.key = maven.modelBuilder.interns +props.24.configurationType = String +props.24.description = Comma-separated list of XML contexts/fields to intern during POM parsing for memory optimization. When not specified, a default set of commonly repeated contexts will be used. Example: "groupId,artifactId,version,scope,type" +props.24.defaultValue = props.24.since = 4.0.0 props.24.configurationSource = User properties -props.25.key = maven.plugin.validation -props.25.configurationType = String -props.25.description = Plugin validation level. -props.25.defaultValue = inline -props.25.since = 3.9.2 +props.25.key = maven.modelBuilder.parallelism +props.25.configurationType = Integer +props.25.description = ProjectBuilder parallelism. +props.25.defaultValue = cores/2 + 1 +props.25.since = 4.0.0 props.25.configurationSource = User properties -props.26.key = maven.plugin.validation.excludes +props.26.key = maven.plugin.validation props.26.configurationType = String -props.26.description = Plugin validation exclusions. -props.26.defaultValue = -props.26.since = 3.9.6 +props.26.description = Plugin validation level. +props.26.defaultValue = inline +props.26.since = 3.9.2 props.26.configurationSource = User properties -props.27.key = maven.project.conf +props.27.key = maven.plugin.validation.excludes props.27.configurationType = String -props.27.description = Maven project configuration directory. -props.27.defaultValue = ${session.rootDirectory}/.mvn -props.27.since = 4.0.0 +props.27.description = Plugin validation exclusions. +props.27.defaultValue = +props.27.since = 3.9.6 props.27.configurationSource = User properties -props.28.key = maven.project.extensions +props.28.key = maven.project.conf props.28.configurationType = String -props.28.description = Maven project extensions. -props.28.defaultValue = ${maven.project.conf}/extensions.xml +props.28.description = Maven project configuration directory. +props.28.defaultValue = ${session.rootDirectory}/.mvn props.28.since = 4.0.0 props.28.configurationSource = User properties -props.29.key = maven.project.settings +props.29.key = maven.project.extensions props.29.configurationType = String -props.29.description = Maven project settings. -props.29.defaultValue = ${maven.project.conf}/settings.xml +props.29.description = Maven project extensions. +props.29.defaultValue = ${maven.project.conf}/extensions.xml props.29.since = 4.0.0 props.29.configurationSource = User properties -props.30.key = maven.relocations.entries +props.30.key = maven.project.settings props.30.configurationType = String -props.30.description = User controlled relocations. This property is a comma separated list of entries with the syntax GAV>GAV. The first GAV can contain \* for any elem (so \*:\*:\* would mean ALL, something you don't want). The second GAV is either fully specified, or also can contain \*, then it behaves as "ordinary relocation": the coordinate is preserved from relocated artifact. Finally, if right hand GAV is absent (line looks like GAV>), the left hand matching GAV is banned fully (from resolving).
Note: the > means project level, while >> means global (whole session level, so even plugins will get relocated artifacts) relocation.
For example,
maven.relocations.entries = org.foo:\*:\*>, \\
org.here:\*:\*>org.there:\*:\*, \\
javax.inject:javax.inject:1>>jakarta.inject:jakarta.inject:1.0.5
means: 3 entries, ban org.foo group (exactly, so org.foo.bar is allowed), relocate org.here to org.there and finally globally relocate (see >> above) javax.inject:javax.inject:1 to jakarta.inject:jakarta.inject:1.0.5. -props.30.defaultValue = +props.30.description = Maven project settings. +props.30.defaultValue = ${maven.project.conf}/settings.xml props.30.since = 4.0.0 props.30.configurationSource = User properties -props.31.key = maven.repo.central +props.31.key = maven.relocations.entries props.31.configurationType = String -props.31.description = Maven central repository URL. The property will have the value of the MAVEN_REPO_CENTRAL environment variable if it is defined. -props.31.defaultValue = https://repo.maven.apache.org/maven2 +props.31.description = User controlled relocations. This property is a comma separated list of entries with the syntax GAV>GAV. The first GAV can contain \* for any elem (so \*:\*:\* would mean ALL, something you don't want). The second GAV is either fully specified, or also can contain \*, then it behaves as "ordinary relocation": the coordinate is preserved from relocated artifact. Finally, if right hand GAV is absent (line looks like GAV>), the left hand matching GAV is banned fully (from resolving).
Note: the > means project level, while >> means global (whole session level, so even plugins will get relocated artifacts) relocation.
For example,
maven.relocations.entries = org.foo:\*:\*>, \\
org.here:\*:\*>org.there:\*:\*, \\
javax.inject:javax.inject:1>>jakarta.inject:jakarta.inject:1.0.5
means: 3 entries, ban org.foo group (exactly, so org.foo.bar is allowed), relocate org.here to org.there and finally globally relocate (see >> above) javax.inject:javax.inject:1 to jakarta.inject:jakarta.inject:1.0.5. +props.31.defaultValue = props.31.since = 4.0.0 props.31.configurationSource = User properties -props.32.key = maven.repo.local +props.32.key = maven.repo.central props.32.configurationType = String -props.32.description = Maven local repository. -props.32.defaultValue = ${maven.user.conf}/repository -props.32.since = 3.0.0 +props.32.description = Maven central repository URL. The property will have the value of the MAVEN_REPO_CENTRAL environment variable if it is defined. +props.32.defaultValue = https://repo.maven.apache.org/maven2 +props.32.since = 4.0.0 props.32.configurationSource = User properties -props.33.key = maven.repo.local.head +props.33.key = maven.repo.local props.33.configurationType = String -props.33.description = User property for chained LRM: the new "head" local repository to use, and "push" the existing into tail. Similar to maven.repo.local.tail, this property may contain comma separated list of paths to be used as local repositories (combine with chained local repository), but while latter is "appending" this one is "prepending". -props.33.defaultValue = -props.33.since = 4.0.0 +props.33.description = Maven local repository. +props.33.defaultValue = ${maven.user.conf}/repository +props.33.since = 3.0.0 props.33.configurationSource = User properties -props.34.key = maven.repo.local.recordReverseTree +props.34.key = maven.repo.local.head props.34.configurationType = String -props.34.description = User property for reverse dependency tree. If enabled, Maven will record ".tracking" directory into local repository with "reverse dependency tree", essentially explaining WHY given artifact is present in local repository. Default: false, will not record anything. -props.34.defaultValue = false -props.34.since = 3.9.0 +props.34.description = User property for chained LRM: the new "head" local repository to use, and "push" the existing into tail. Similar to maven.repo.local.tail, this property may contain comma separated list of paths to be used as local repositories (combine with chained local repository), but while latter is "appending" this one is "prepending". +props.34.defaultValue = +props.34.since = 4.0.0 props.34.configurationSource = User properties -props.35.key = maven.repo.local.tail +props.35.key = maven.repo.local.recordReverseTree props.35.configurationType = String -props.35.description = User property for chained LRM: list of "tail" local repository paths (separated by comma), to be used with org.eclipse.aether.util.repository.ChainedLocalRepositoryManager. Default value: null, no chained LRM is used. -props.35.defaultValue = +props.35.description = User property for reverse dependency tree. If enabled, Maven will record ".tracking" directory into local repository with "reverse dependency tree", essentially explaining WHY given artifact is present in local repository. Default: false, will not record anything. +props.35.defaultValue = false props.35.since = 3.9.0 props.35.configurationSource = User properties -props.36.key = maven.repo.local.tail.ignoreAvailability +props.36.key = maven.repo.local.tail props.36.configurationType = String -props.36.description = User property for chained LRM: whether to ignore "availability check" in tail or not. Usually you do want to ignore it. This property is mapped onto corresponding Resolver 2.x property, is like a synonym for it. Default value: true. +props.36.description = User property for chained LRM: list of "tail" local repository paths (separated by comma), to be used with org.eclipse.aether.util.repository.ChainedLocalRepositoryManager. Default value: null, no chained LRM is used. props.36.defaultValue = props.36.since = 3.9.0 props.36.configurationSource = User properties -props.37.key = maven.resolver.dependencyManagerTransitivity +props.37.key = maven.repo.local.tail.ignoreAvailability props.37.configurationType = String -props.37.description = User property for selecting dependency manager behaviour regarding transitive dependencies and dependency management entries in their POMs. Maven 3 targeted full backward compatibility with Maven2, hence it ignored dependency management entries in transitive dependency POMs. Maven 4 enables "transitivity" by default, hence unlike Maven2, obeys dependency management entries deep in dependency graph as well.
Default: "true". -props.37.defaultValue = true -props.37.since = 4.0.0 +props.37.description = User property for chained LRM: whether to ignore "availability check" in tail or not. Usually you do want to ignore it. This property is mapped onto corresponding Resolver 2.x property, is like a synonym for it. Default value: true. +props.37.defaultValue = +props.37.since = 3.9.0 props.37.configurationSource = User properties -props.38.key = maven.resolver.transport +props.38.key = maven.resolver.dependencyManagerTransitivity props.38.configurationType = String -props.38.description = Resolver transport to use. Can be default, wagon, apache, jdk or auto. -props.38.defaultValue = default +props.38.description = User property for selecting dependency manager behaviour regarding transitive dependencies and dependency management entries in their POMs. Maven 3 targeted full backward compatibility with Maven2, hence it ignored dependency management entries in transitive dependency POMs. Maven 4 enables "transitivity" by default, hence unlike Maven2, obeys dependency management entries deep in dependency graph as well.
Default: "true". +props.38.defaultValue = true props.38.since = 4.0.0 props.38.configurationSource = User properties -props.39.key = maven.session.versionFilter +props.39.key = maven.resolver.transport props.39.configurationType = String -props.39.description = User property for version filter expression used in session, applied to resolving ranges: a semicolon separated list of filters to apply. By default, no version filter is applied (like in Maven 3).
Supported filters:
  • "h" or "h(num)" - highest version or top list of highest ones filter
  • "l" or "l(num)" - lowest version or bottom list of lowest ones filter
  • "s" - contextual snapshot filter
  • "e(G:A:V)" - predicate filter (leaves out G:A:V from range, if hit, V can be range)
Example filter expression: "h(5);s;e(org.foo:bar:1) will cause: ranges are filtered for "top 5" (instead full range), snapshots are banned if root project is not a snapshot, and if range for org.foo:bar is being processed, version 1 is omitted. Value in this property builds org.eclipse.aether.collection.VersionFilter instance. -props.39.defaultValue = +props.39.description = Resolver transport to use. Can be default, wagon, apache, jdk or auto. +props.39.defaultValue = default props.39.since = 4.0.0 props.39.configurationSource = User properties -props.40.key = maven.settings.security +props.40.key = maven.session.versionFilter props.40.configurationType = String -props.40.description = -props.40.defaultValue = ${maven.user.conf}/settings-security4.xml +props.40.description = User property for version filter expression used in session, applied to resolving ranges: a semicolon separated list of filters to apply. By default, no version filter is applied (like in Maven 3).
Supported filters:
  • "h" or "h(num)" - highest version or top list of highest ones filter
  • "l" or "l(num)" - lowest version or bottom list of lowest ones filter
  • "s" - contextual snapshot filter
  • "e(G:A:V)" - predicate filter (leaves out G:A:V from range, if hit, V can be range)
Example filter expression: "h(5);s;e(org.foo:bar:1) will cause: ranges are filtered for "top 5" (instead full range), snapshots are banned if root project is not a snapshot, and if range for org.foo:bar is being processed, version 1 is omitted. Value in this property builds org.eclipse.aether.collection.VersionFilter instance. +props.40.defaultValue = +props.40.since = 4.0.0 props.40.configurationSource = User properties -props.41.key = maven.startInstant -props.41.configurationType = java.time.Instant -props.41.description = User property used to store the build timestamp. -props.41.defaultValue = -props.41.since = 4.0.0 +props.41.key = maven.settings.security +props.41.configurationType = String +props.41.description = +props.41.defaultValue = ${maven.user.conf}/settings-security4.xml props.41.configurationSource = User properties -props.42.key = maven.style.color -props.42.configurationType = String -props.42.description = Maven output color mode. Allowed values are auto, always, never. -props.42.defaultValue = auto +props.42.key = maven.startInstant +props.42.configurationType = java.time.Instant +props.42.description = User property used to store the build timestamp. +props.42.defaultValue = props.42.since = 4.0.0 props.42.configurationSource = User properties -props.43.key = maven.style.debug +props.43.key = maven.style.color props.43.configurationType = String -props.43.description = Color style for debug messages. -props.43.defaultValue = bold,f:cyan +props.43.description = Maven output color mode. Allowed values are auto, always, never. +props.43.defaultValue = auto props.43.since = 4.0.0 props.43.configurationSource = User properties -props.44.key = maven.style.error +props.44.key = maven.style.debug props.44.configurationType = String -props.44.description = Color style for error messages. -props.44.defaultValue = bold,f:red +props.44.description = Color style for debug messages. +props.44.defaultValue = bold,f:cyan props.44.since = 4.0.0 props.44.configurationSource = User properties -props.45.key = maven.style.failure +props.45.key = maven.style.error props.45.configurationType = String -props.45.description = Color style for failure messages. +props.45.description = Color style for error messages. props.45.defaultValue = bold,f:red props.45.since = 4.0.0 props.45.configurationSource = User properties -props.46.key = maven.style.info +props.46.key = maven.style.failure props.46.configurationType = String -props.46.description = Color style for info messages. -props.46.defaultValue = bold,f:blue +props.46.description = Color style for failure messages. +props.46.defaultValue = bold,f:red props.46.since = 4.0.0 props.46.configurationSource = User properties -props.47.key = maven.style.mojo +props.47.key = maven.style.info props.47.configurationType = String -props.47.description = Color style for mojo messages. -props.47.defaultValue = f:green +props.47.description = Color style for info messages. +props.47.defaultValue = bold,f:blue props.47.since = 4.0.0 props.47.configurationSource = User properties -props.48.key = maven.style.project +props.48.key = maven.style.mojo props.48.configurationType = String -props.48.description = Color style for project messages. -props.48.defaultValue = f:cyan +props.48.description = Color style for mojo messages. +props.48.defaultValue = f:green props.48.since = 4.0.0 props.48.configurationSource = User properties -props.49.key = maven.style.strong +props.49.key = maven.style.project props.49.configurationType = String -props.49.description = Color style for strong messages. -props.49.defaultValue = bold +props.49.description = Color style for project messages. +props.49.defaultValue = f:cyan props.49.since = 4.0.0 props.49.configurationSource = User properties -props.50.key = maven.style.success +props.50.key = maven.style.strong props.50.configurationType = String -props.50.description = Color style for success messages. -props.50.defaultValue = bold,f:green +props.50.description = Color style for strong messages. +props.50.defaultValue = bold props.50.since = 4.0.0 props.50.configurationSource = User properties -props.51.key = maven.style.trace +props.51.key = maven.style.success props.51.configurationType = String -props.51.description = Color style for trace messages. -props.51.defaultValue = bold,f:magenta +props.51.description = Color style for success messages. +props.51.defaultValue = bold,f:green props.51.since = 4.0.0 props.51.configurationSource = User properties -props.52.key = maven.style.transfer +props.52.key = maven.style.trace props.52.configurationType = String -props.52.description = Color style for transfer messages. -props.52.defaultValue = f:bright-black +props.52.description = Color style for trace messages. +props.52.defaultValue = bold,f:magenta props.52.since = 4.0.0 props.52.configurationSource = User properties -props.53.key = maven.style.warning +props.53.key = maven.style.transfer props.53.configurationType = String -props.53.description = Color style for warning messages. -props.53.defaultValue = bold,f:yellow +props.53.description = Color style for transfer messages. +props.53.defaultValue = f:bright-black props.53.since = 4.0.0 props.53.configurationSource = User properties -props.54.key = maven.user.conf +props.54.key = maven.style.warning props.54.configurationType = String -props.54.description = Maven user configuration directory. -props.54.defaultValue = ${user.home}/.m2 +props.54.description = Color style for warning messages. +props.54.defaultValue = bold,f:yellow props.54.since = 4.0.0 props.54.configurationSource = User properties -props.55.key = maven.user.extensions +props.55.key = maven.user.conf props.55.configurationType = String -props.55.description = Maven user extensions. -props.55.defaultValue = ${maven.user.conf}/extensions.xml +props.55.description = Maven user configuration directory. +props.55.defaultValue = ${user.home}/.m2 props.55.since = 4.0.0 props.55.configurationSource = User properties -props.56.key = maven.user.settings +props.56.key = maven.user.extensions props.56.configurationType = String -props.56.description = Maven user settings. -props.56.defaultValue = ${maven.user.conf}/settings.xml +props.56.description = Maven user extensions. +props.56.defaultValue = ${maven.user.conf}/extensions.xml props.56.since = 4.0.0 props.56.configurationSource = User properties -props.57.key = maven.user.toolchains +props.57.key = maven.user.settings props.57.configurationType = String -props.57.description = Maven user toolchains. -props.57.defaultValue = ${maven.user.conf}/toolchains.xml +props.57.description = Maven user settings. +props.57.defaultValue = ${maven.user.conf}/settings.xml props.57.since = 4.0.0 props.57.configurationSource = User properties -props.58.key = maven.version +props.58.key = maven.user.toolchains props.58.configurationType = String -props.58.description = Maven version. -props.58.defaultValue = -props.58.since = 3.0.0 -props.58.configurationSource = system_properties -props.59.key = maven.version.major +props.58.description = Maven user toolchains. +props.58.defaultValue = ${maven.user.conf}/toolchains.xml +props.58.since = 4.0.0 +props.58.configurationSource = User properties +props.59.key = maven.version props.59.configurationType = String -props.59.description = Maven major version: contains the major segment of this Maven version. +props.59.description = Maven version. props.59.defaultValue = -props.59.since = 4.0.0 +props.59.since = 3.0.0 props.59.configurationSource = system_properties -props.60.key = maven.version.minor +props.60.key = maven.version.major props.60.configurationType = String -props.60.description = Maven minor version: contains the minor segment of this Maven version. +props.60.description = Maven major version: contains the major segment of this Maven version. props.60.defaultValue = props.60.since = 4.0.0 props.60.configurationSource = system_properties -props.61.key = maven.version.patch +props.61.key = maven.version.minor props.61.configurationType = String -props.61.description = Maven patch version: contains the patch segment of this Maven version. +props.61.description = Maven minor version: contains the minor segment of this Maven version. props.61.defaultValue = props.61.since = 4.0.0 props.61.configurationSource = system_properties -props.62.key = maven.version.snapshot +props.62.key = maven.version.patch props.62.configurationType = String -props.62.description = Maven snapshot: contains "true" if this Maven is a snapshot version. +props.62.description = Maven patch version: contains the patch segment of this Maven version. props.62.defaultValue = props.62.since = 4.0.0 props.62.configurationSource = system_properties -props.63.key = maven.versionRangeResolver.natureOverride +props.63.key = maven.version.snapshot props.63.configurationType = String -props.63.description = Configuration property for version range resolution used metadata "nature". It may contain following string values:
  • "auto" - decision done based on range being resolver: if any boundary is snapshot, use "release_or_snapshot", otherwise "release"
  • "release_or_snapshot" - the default
  • "release" - query only release repositories to discover versions
  • "snapshot" - query only snapshot repositories to discover versions
Default (when unset) is existing Maven behaviour: "release_or_snapshots". -props.63.defaultValue = release_or_snapshot +props.63.description = Maven snapshot: contains "true" if this Maven is a snapshot version. +props.63.defaultValue = props.63.since = 4.0.0 -props.63.configurationSource = User properties -props.64.key = maven.versionResolver.noCache -props.64.configurationType = Boolean -props.64.description = User property for disabling version resolver cache. -props.64.defaultValue = false -props.64.since = 3.0.0 +props.63.configurationSource = system_properties +props.64.key = maven.versionRangeResolver.natureOverride +props.64.configurationType = String +props.64.description = Configuration property for version range resolution used metadata "nature". It may contain following string values:
  • "auto" - decision done based on range being resolver: if any boundary is snapshot, use "release_or_snapshot", otherwise "release"
  • "release_or_snapshot" - the default
  • "release" - query only release repositories to discover versions
  • "snapshot" - query only snapshot repositories to discover versions
Default (when unset) is existing Maven behaviour: "release_or_snapshots". +props.64.defaultValue = release_or_snapshot +props.64.since = 4.0.0 props.64.configurationSource = User properties +props.65.key = maven.versionResolver.noCache +props.65.configurationType = Boolean +props.65.description = User property for disabling version resolver cache. +props.65.defaultValue = false +props.65.since = 3.0.0 +props.65.configurationSource = User properties diff --git a/src/site/markdown/configuration.yaml b/src/site/markdown/configuration.yaml index 66431020a948..7e98582bd565 100644 --- a/src/site/markdown/configuration.yaml +++ b/src/site/markdown/configuration.yaml @@ -158,6 +158,12 @@ props: defaultValue: false since: 4.0.0 configurationSource: User properties + - key: maven.modelBuilder.interns + configurationType: String + description: "Comma-separated list of XML contexts/fields to intern during POM parsing for memory optimization. When not specified, a default set of commonly repeated contexts will be used. Example: \"groupId,artifactId,version,scope,type\"" + defaultValue: + since: 4.0.0 + configurationSource: User properties - key: maven.modelBuilder.parallelism configurationType: Integer description: "ProjectBuilder parallelism." diff --git a/src/site/markdown/maven-configuration.md b/src/site/markdown/maven-configuration.md index 8be5e66f99c1..0f27cff6b426 100644 --- a/src/site/markdown/maven-configuration.md +++ b/src/site/markdown/maven-configuration.md @@ -54,6 +54,7 @@ To modify this file, edit the template and regenerate. | `maven.logger.showThreadName` | `Boolean` | Set to true if you want to output the current thread name. Defaults to true. | `true` | 4.0.0 | User properties | | `maven.logger.warnLevelString` | `String` | The string value output for the warn level. Defaults to WARN. | `WARN` | 4.0.0 | User properties | | `maven.maven3Personality` | `Boolean` | User property for controlling "maven personality". If activated Maven will behave as previous major version, Maven 3. | `false` | 4.0.0 | User properties | +| `maven.modelBuilder.interns` | `String` | Comma-separated list of XML contexts/fields to intern during POM parsing for memory optimization. When not specified, a default set of commonly repeated contexts will be used. Example: "groupId,artifactId,version,scope,type" | - | 4.0.0 | User properties | | `maven.modelBuilder.parallelism` | `Integer` | ProjectBuilder parallelism. | `cores/2 + 1` | 4.0.0 | User properties | | `maven.plugin.validation` | `String` | Plugin validation level. | `inline` | 3.9.2 | User properties | | `maven.plugin.validation.excludes` | `String` | Plugin validation exclusions. | - | 3.9.6 | User properties |