What do addDefaultImplementationEntries / addDefaultSpecificationEntries do, and what Implementation-* headers result?
answer
- addDefaultImplementationEntries => Implementation-Title/Version/Vendor
- addDefaultSpecificationEntries => Specification-*
- values come from POM name/version/organization
- Package.getImplementationVersion()
- null when run from exploded classes, not JAR
basics
~10 sSetting <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 sUnder <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<archive>
<manifest>
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
<addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
</manifest>
</archive>go deeper
Know the flag adds version/title/vendor headers from the POM.
Know which exact headers each flag writes and how to read them via the Package API.
Know the exploded-classes null pitfall and how to enrich with Git SHA/build time for provenance.
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