Skip to content

[MNG-8537] Keep Maven Namespace the same#10952

Open
elharo wants to merge 53 commits intomasterfrom
namespace
Open

[MNG-8537] Keep Maven Namespace the same#10952
elharo wants to merge 53 commits intomasterfrom
namespace

Conversation

@elharo
Copy link
Copy Markdown
Contributor

@elharo elharo commented Jul 19, 2025

Namespaces and model versions are not the same thing and do not serve the same purpose.

This PR has two related effects:

  1. The namespace for all pom.xml files is now http://maven.apache.org/POM/4.0.0
  2. Namespaces are not used to infer model versions. That's the job of the modelVersion element

Fixing this opens up Maven to the large and valuable XML toolchain. It makes it much easier for developers to analyze, store, and manipulate pom.xml files from a variety of languages without depending on complex Maven APIs and tooling. It decouples the poms from the Java source code. It is no longer necessary to use Maven libraries to process poms.

On the Maven side, it removes a roadblock to using standard XML parsers like Xerces instead of maintaining a separate custom XML parser. The more we can fit into the standard toolchains, the easier development becomes.

fixes #10692

@elharo elharo changed the title Revert Namespace [MNG-8537] Revert Namespace Jul 19, 2025
@elharo elharo marked this pull request as ready for review July 20, 2025 10:58
@elharo elharo changed the title [MNG-8537] Revert Namespace [MNG-8537] Keep Maven Namespace the same Jul 20, 2025
@elharo elharo requested a review from Bukama July 20, 2025 11:24
@gnodet
Copy link
Copy Markdown
Contributor

gnodet commented Mar 18, 2026

Review: Missing spots where namespace is still derived from modelVersion

The PR changes the assumption so that the XML namespace is always http://maven.apache.org/POM/4.0.0 regardless of modelVersion, but several places still derive the namespace from the modelVersion:

1. TransformerSupport.write() — Consumer/Build POM writer (most critical)

File: impl/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/TransformerSupport.java (lines 67, 82-83)

protected static final String NAMESPACE_FORMAT = "http://maven.apache.org/POM/%s";
// ...
writer.setNamespace(String.format(NAMESPACE_FORMAT, version));
writer.setSchemaLocation(String.format(SCHEMA_LOCATION_FORMAT, version));

Every consumer POM deployed to a repository will have a version-derived namespace (e.g. http://maven.apache.org/POM/4.1.0). The namespace should be fixed to http://maven.apache.org/POM/4.0.0 while the schema location can remain version-specific.

2. DefaultModelBuilder — modelVersion inferred from namespace

File: impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelBuilder.java (lines 1565-1570)

if (model.getModelVersion() == null) {
    String namespace = model.getNamespaceUri();
    if (namespace != null && namespace.startsWith(NAMESPACE_PREFIX)) {
        model = model.withModelVersion(namespace.substring(NAMESPACE_PREFIX.length()));
    }
}

This strips the prefix http://maven.apache.org/POM/ to derive the modelVersion. With a fixed namespace this will always infer 4.0.0, which may be the correct new behavior, but it's worth double-checking. Old POMs in the wild with xmlns="http://maven.apache.org/POM/4.1.0" and no <modelVersion> element would previously be detected as 4.1.0 but will now silently become 4.0.0.

3. maven.mdo — Modello model definition

File: api/maven-api-model/src/main/mdo/maven.mdo (lines 48-49)

xml.namespace="http://maven.apache.org/POM/${version}"
xml.schemaLocation="https://maven.apache.org/xsd/maven-${version}.xsd"

The namespace template uses ${version}. Currently this generates the default for the MavenStaxWriter class. Since the base model version is 4.0.0, the default happens to be correct, but conceptually the namespace should no longer be version-dependent. Consider hardcoding the namespace while keeping the schema location version-dependent.

4. DefaultModelXmlFactory.read() — Namespace validation is permissive

File: impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultModelXmlFactory.java (lines 65-71)

if (isModelVersionGreaterThan400(model)
        && !model.getNamespaceUri().startsWith("http://maven.apache.org/POM/")) {

This still allows http://maven.apache.org/POM/4.1.0 to pass validation. If the intent is to enforce a single namespace, this should validate more strictly (e.g. check for exact equality with http://maven.apache.org/POM/4.0.0). However, for backward compatibility with already-deployed POMs using 4.1.0 namespace, the permissive check may be intentional — worth documenting.

Summary

Priority File Issue
Critical TransformerSupport.java Consumer/build POM writer uses String.format(NAMESPACE_FORMAT, version) — will write wrong namespace
High DefaultModelBuilder.java modelVersion inference from namespace — behavior change for old 4.1.0-namespaced POMs without explicit modelVersion
Medium maven.mdo Namespace template is conceptually version-dependent
Low DefaultModelXmlFactory.java Namespace validation still allows old versioned namespaces

@gnodet
Copy link
Copy Markdown
Contributor

gnodet commented Mar 18, 2026

Two additional findings I missed above

5. MavenStaxReader (generated) — no enforcement of fixed namespace

Template: src/mdo/reader-stax.vm (lines 517-528)

The generated checkNamespace() method only checks that child elements use the same namespace as the root element — it doesn't enforce that the namespace must be http://maven.apache.org/POM/4.0.0. A POM with xmlns="http://maven.apache.org/POM/4.1.0" will be read successfully and that namespace stored on the model. This creates an inconsistency: reading a POM with 4.1.0 namespace succeeds, but writing it back via TransformerSupport (issue #1) would produce a different namespace based on the modelVersion.

If the intent is a single fixed namespace, the reader should either reject non-4.0.0 namespaces or normalize them on read.

6. ModelVersionUtils.getSchemaLocationForModelVersion() — uses domtrip library constants

File: impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/ModelVersionUtils.java (lines 234-244)

This method uses the schema location constants from MavenPomElements.SchemaLocations (domtrip library), which embed the namespace in the schema location string (e.g. http://maven.apache.org/POM/4.1.0 https://maven.apache.org/xsd/maven-4.1.0.xsd). The new UpgradeConstants.SchemaLocations correctly uses the fixed namespace, but ModelVersionUtils still references the domtrip constants which may have the old version-derived namespace in the xsi:schemaLocation value.

@elharo
Copy link
Copy Markdown
Contributor Author

elharo commented Mar 19, 2026

something in the maven-shade-plugin is tripping a place I've missed:

Error:  Failed to execute goal org.apache.maven.plugins:maven-shade-plugin:3.5.0:shade (default) on project test: Error creating shaded jar: 1 problem was 
Error:      - [FATAL] Non-parseable POM /Users/runner/work/maven/maven/its/core-it-suite/target/test-classes/mng-7228-leaky-model/target/dependency-reduced-pom.xml: Invalid namespace 'http://maven.apache.org/POM/4.1.0' for model version 4.1.0 for project  at /Users/runner/work/maven/maven/its/core-it-suite/target/test-classes/mng-7228-leaky-model/target/dependency-reduced-pom.xml
Error:  -> [Help 1]

@elharo
Copy link
Copy Markdown
Contributor Author

elharo commented Mar 20, 2026

working on the first four.

On #5 I see the point. It's not immediately obvious to me whether ns or namespace is the namespace of the element. Digging.

    private void checkNamespace(XMLStreamReader parser, boolean strict, String namespace) throws XMLStreamException {
        if (strict) {
            String ns = parser.getNamespaceURI();
            if (!Objects.equals(namespace, ns)) {
                throw new XMLStreamException(
                    String.format("Unexpected namespace for element '%s': found '%s' but expected '%s'",
                        parser.getLocalName(),
                        ns != null ? ns : "no namespace",
                        namespace),
                    parser.getLocation(),
                    null
                );
            }
        }
    }

@elharo
Copy link
Copy Markdown
Contributor Author

elharo commented Mar 20, 2026

I think ChatGPT 4.1 found the cause of the remaining test failure. It's in the maven-shade-plugin where PomWriter tries to construct a namespace rather than simply copying the namespace of the source document:

https://github.com/apache/maven-shade-plugin/blob/b1b39fda298ab71b7194f30de443bdc90852b60e/src/main/java/org/apache/maven/plugins/shade/pom/PomWriter.java

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[MNG-8537] Maven 4 namespace should not change

3 participants