skip to content

What do addDefaultImplementationEntries / addDefaultSpecificationEntries do, and what Implementation-* headers result?

level: middleimportance: should knowfreq 40%

answer

  1. addDefaultImplementationEntries => Implementation-Title/Version/Vendor
  2. addDefaultSpecificationEntries => Specification-*
  3. values come from POM name/version/organization
  4. Package.getImplementationVersion()
  5. null when run from exploded classes, not JAR

basics

~10 s

Setting <addDefaultImplementationEntries>true</addDefaultImplementationEntries> tells maven-jar-plugin to write Implementation-Title/Version/Vendor headers from your pom's name, version, and organization, so code can read its own version at runtime.

solid answer

~40 s

Under <archive><manifest> you can enable <addDefaultImplementationEntries>true</addDefaultImplementationEntries> and <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>. These populate the manifest from your POM: implementation entries write Implementation-Title (artifactId or name), Implementation-Version (project version), and Implementation-Vendor (organization name); specification entries write Specification-Title/Version/Vendor similarly. Their value is letting code introspect its own build at runtime via Package.getImplementationVersion() / getImplementationTitle(), which the JVM reads from the sealed package's manifest headers. This is the clean, no-hardcoding way to expose a version in logs, an /actuator/info endpoint, or an About dialog. You can override or add to these with <manifestEntries> (e.g. a Git commit via buildnumber-maven-plugin). Note version reflection only works for code loaded from a JAR with these headers, not from the IDE's exploded classpath.

code

xml · 6 lines
xml
<archive>
  <manifest>
    <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
    <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
  </manifest>
</archive>

go deeper

for a junior

Know the flag adds version/title/vendor headers from the POM.

for a middle

Know which exact headers each flag writes and how to read them via the Package API.

for a senior

Know the exploded-classes null pitfall and how to enrich with Git SHA/build time for provenance.

for a principal

Mandate consistent build provenance headers across artifact types (jar/war/shade) via parent-pom plugin management for traceability.

## The problem they solve Applications often need to report their own version ("v2.3.1") in logs, health endpoints, or a UI. Hardcoding it duplicates the POM version. The manifest's `Implementation-*` and `Specification-*` headers let the JVM expose that at runtime instead. ## The two flags Inside the maven-jar-plugin config: ```xml <archive> <manifest> <addDefaultImplementationEntries>true</addDefaultImplementationEntries> <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries> </manifest> </archive> ``` - **`addDefaultImplementationEntries`** writes: - `Implementation-Title` — from `<name>` (or artifactId) - `Implementation-Version` — from `<version>` - `Implementation-Vendor` — from `<organization><name>` - **`addDefaultSpecificationEntries`** writes the parallel `Specification-Title`, `Specification-Version`, `Specification-Vendor`. *Specification* vs *Implementation* mirrors the Java versioning convention: spec = the API/contract version, implementation = the concrete build of it. ## Reading them at runtime The JVM associates manifest headers with the **`Package`** object of classes loaded from that JAR: ```java String v = MyApp.class.getPackage().getImplementationVersion(); ``` This returns the `Implementation-Version` header — but only when the class was loaded **from the JAR** that contains the manifest. Running from an IDE's exploded `target/classes` returns `null`, which surprises people. ## Combining with custom entries For richer provenance, add a build number / Git SHA: ```xml <archive> <manifest> <addDefaultImplementationEntries>true</addDefaultImplementationEntries> </manifest> <manifestEntries> <Build-Time>${maven.build.timestamp}</Build-Time> <Git-Commit>${buildNumber}</Git-Commit> </manifestEntries> </archive> ``` (`${buildNumber}` typically supplied by buildnumber-maven-plugin.) ## Practical notes - Many plugins (assembly, shade, war) accept the same `<archive>` config, so enable these consistently per artifact type. - For sealed/signed packages and split-package issues, these headers also drive package sealing checks, but their everyday use is version reflection.

  • Where do the values for Implementation-Version and Implementation-Vendor come from?
    From the POM: version maps to Implementation-Version, and <organization><name> maps to Implementation-Vendor; the title from <name>/artifactId.
  • Why might Package.getImplementationVersion() return null in your tests?
    Because the class was loaded from an exploded target/classes (IDE/Surefire) rather than a JAR containing the manifest headers.

saying these in an interview costs you the question

  • Confusing Implementation-* (concrete build) with Specification-* (API/contract version)
  • Believing the version is readable when running from exploded classes, not a packaged JAR
  • Hardcoding the version in code instead of reading it from the manifest

context