How Maven Artifact Transformation and Consumer POM Generation Works

Maven generates a lightweight consumer POM by transforming the original project model, registering it as a classified artifact with the consumer classifier, and substituting it during install and deploy phases to hide internal build details from downstream projects.

Apache Maven's artifact transformation and consumer POM generation creates a simplified, consumption-ready Project Object Model (POM) that downstream builds resolve instead of the full build POM. This mechanism, implemented in the apache/maven repository, strips build-specific metadata and optionally flattens dependency management to produce a cleaner transitive dependency graph. The process involves two primary components working in concert: the ConsumerPomArtifactTransformer which manages artifact registration, and the DefaultConsumerPomBuilder which handles the actual model transformation.

Core Components of the Transformation Pipeline

The transformation pipeline splits responsibilities between artifact handling and model building. Understanding these classes is essential for debugging or extending Maven's consumer POM behavior.

ConsumerPomArtifactTransformer

Located in impl/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/ConsumerPomArtifactTransformer.java, this class serves as the entry point for the transformation process. It detects when the consumer POM feature is enabled via Features.consumerPom(session.getConfigProperties()), creates a temporary file named consumer-*.pom, and registers a new artifact with the classifier consumer. During the install and deploy phases, the replacePom method rewrites the artifact set so that the consumer POM replaces the normal POM, and optionally adds a second artifact with classifier build when the deployBuildPom flag is true.

DefaultConsumerPomBuilder

Found in impl/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/DefaultConsumerPomBuilder.java, this class implements the core transformation logic. It handles three major concerns: dependency flattening (merging managed dependencies into direct ones), model-version handling (downgrading to Maven 3-compatible modelVersion 4.0.0 or preserving 4.2.0), and pruning (removing build-specific sections like mailing lists, issue management, and empty profiles).

The Transformation Process Step-by-Step

Maven's artifact transformation follows a strict pipeline from feature detection to artifact substitution.

Feature Detection and Activation

The process begins when ConsumerPomArtifactTransformer.injectTransformedArtifacts detects the consumer POM feature flag. When enabled, the method creates a temporary file and registers a TransformedArtifact with the consumer classifier. Temporary files are tracked in a thread-safe CopyOnWriteArraySet and cleaned up via the @PreDestroy hook (doDeleteFiles).

Model Building and Path Selection

The ConsumerPomArtifactTransformer.transform method delegates to PomBuilder.build, which selects a transformation path based on packaging type:

  • POM packaging (pom) → buildPom (calls transformPom)
  • BOM packaging (bom) → buildBom or buildBomWithoutFlatten
  • Non-POM (e.g., jar) → buildNonPom (calls transformNonPom)

Dependency Flattening and Managed Dependencies

When maven.consumer.pom.flatten=true, the builder merges dependencyManagement entries into direct dependencies, applying version, scope, optional, and exclusion data. It also discards any managed entries that are not actually used (see lines 222-274 in DefaultConsumerPomBuilder.java). When flattening is disabled, the model is kept as-is, but BOM packaging still changes to pom.

Model Version Handling and Compatibility

The builder handles model version compatibility through the preserve.model.version property. When mixins are present, Maven either throws an exception unless preserve.model.version=true or flattening is enabled, or it downgrades the model version to 4.0.0 for Maven 3 compatibility (lines 68-84). If preservation is requested, the newer 4.2.0 model version is maintained.

Pruning Build-Specific Metadata

The prune helpers (lines 560-618) strip out consumption-irrelevant sections:

  • Central repository entries (kept only if not central)
  • Build-specific sections (mailing lists, issue management, SCM child-inheritance)
  • Empty profiles (verified via isEmpty)
  • Plugin repositories

Artifact Substitution for Install and Deploy

After the transformed model is written, ConsumerPomArtifactTransformer.replacePom substitutes the original POM artifact with the consumer artifact (no classifier). If deployBuildPom is enabled, Maven attaches a second artifact with classifier build containing the original POM. This ensures repositories contain both the consumer-friendly POM and the full build POM when required.

Configuration and Usage Examples

Enable the consumer POM feature and flattening from the command line:


# Activate consumer-POM generation and flattening

mvn clean install -DconsumerPom=true -Dmaven.consumer.pom.flatten=true

Inspect the generated consumer POM using the dependency tree plugin:


# View the consumer POM as a classified artifact

mvn dependency:tree -Dclassifier=consumer

Reference the consumer POM in downstream projects:

<dependency>
    <groupId>com.example</groupId>
    <artifactId>my-library</artifactId>
    <version>1.2.3</version>
    <!-- No classifier needed; Maven resolves the consumer POM automatically -->
</dependency>

Summary

  • Maven artifact transformation creates a lightweight consumer POM by processing the original model through DefaultConsumerPomBuilder.
  • The ConsumerPomArtifactTransformer manages artifact registration, creates temporary consumer-*.pom files, and handles substitution during install/deploy.
  • Dependency flattening merges managed dependencies into direct dependencies when maven.consumer.pom.flatten=true, reducing transitive dependency noise.
  • Model pruning removes central repositories, mailing lists, issue management, and empty profiles to hide internal build details.
  • The classifier consumer identifies the transformed artifact, while the optional build classifier preserves the original POM when deployBuildPom is enabled.
  • Temporary files are managed in a thread-safe set and cleaned up via @PreDestroy hooks.

Frequently Asked Questions

What is the difference between the build POM and the consumer POM?

The build POM contains complete project metadata including build plugins, plugin repositories, mailing lists, and internal profiles required to build the project from source. The consumer POM is a transformed, lightweight version that strips these build-specific sections and optionally flattens dependency management, providing downstream projects with only the essential information needed for dependency resolution.

How do I enable dependency flattening in the consumer POM?

Set the system property maven.consumer.pom.flatten=true when running Maven. This instructs DefaultConsumerPomBuilder to merge entries from dependencyManagement into direct dependencies, applying managed versions, scopes, and exclusions directly to the dependency list. This reduces the complexity of transitive dependency resolution for consumers.

Why does Maven downgrade the model version to 4.0.0?

Maven downgrades to modelVersion 4.0.0 to ensure compatibility with Maven 3 consumers when the project uses mixins or when preserve.model.version is not set to true. This occurs in DefaultConsumerPomBuilder (lines 68-84) to prevent Maven 3 installations from failing to parse newer 4.2.0 model features. Set -Dpreserve.model.version=true to retain the newer model version when all consumers use Maven 4.

Can I deploy both the consumer POM and the original build POM?

Yes. When the deployBuildPom flag is enabled, ConsumerPomArtifactTransformer.replacePom registers two artifacts: the consumer POM (with no classifier) and the original build POM (with classifier build). This allows repository consumers to use the lightweight POM by default while still preserving the full build POM for tools that require complete project metadata.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →