skip to content

When publishing a library, why must each additional jar (sources, javadoc, etc.) set a distinct archiveClassifier, and what goes wrong if two jars share the same classifier?

level: middleimportance: must knowfreq 50%

answer

  1. one GAV, classifier disambiguates
  2. sources / javadoc / tests classifiers
  3. duplicate classifier+extension = collision
  4. withSourcesJar() / withJavadocJar()
  5. task archiveClassifier must match publication

basics

~20 s

All jars in one publication share the same group:name:version, so the classifier is the only thing that distinguishes them. Two jars with the same classifier collide — the publication fails or one overwrites the other.

solid answer

~50 s

A Maven/Ivy publication groups several artifacts under one set of coordinates (`group:name:version`). The **classifier** (and extension) is what differentiates files within that GAV — `mylib-1.0.jar`, `mylib-1.0-sources.jar`, `mylib-1.0-javadoc.jar`. Each secondary `Jar` task you register must therefore set a unique `archiveClassifier` (`sources`, `javadoc`, `tests`, etc.); the main jar keeps an empty classifier. If two artifacts end up with the same classifier+extension, the publication has a collision: `maven-publish` will fail validation (duplicate artifact) or, depending on how artifacts are added, one silently overwrites the other and consumers can't request the intended file. Set the classifier with `archiveClassifier.set("sources")` inside the task. Note that the classifier on the **artifact** in the publication and the `archiveClassifier` on the producing task must stay consistent — usually you just register the jar with the right classifier and add it to the component or publication, letting Gradle derive the classifier from the task.

code

kotlin · 10 lines
kotlin
java {
    withSourcesJar()   // registers sourcesJar with archiveClassifier 'sources'
    withJavadocJar()   // registers javadocJar with archiveClassifier 'javadoc'
}

// equivalent manual form:
tasks.register<Jar>("sourcesJar") {
    archiveClassifier.set("sources")
    from(sourceSets.main.get().allSource)
}

go deeper

for a junior

State that secondary jars need unique classifiers and name sources/javadoc.

for a middle

Explain the one-GAV model, that classifier+extension disambiguates, and show withSourcesJar()/withJavadocJar() plus the manual equivalent.

for a senior

Describe collision behavior (validation failure vs overwrite) and the need to keep task archiveClassifier consistent with the published artifact classifier.

for a principal

Define a standard set of secondary artifacts and classifier conventions in a publishing convention plugin so all library modules expose sources/javadoc uniformly.

## One coordinate, many files When you publish, all artifacts in a single `MavenPublication` live under the same `group:name:version` (GAV). A repository stores them side by side. What tells them apart is the **classifier** plus the **extension**: ``` mylib-1.0.jar (main, classifier = "") mylib-1.0-sources.jar (classifier = sources) mylib-1.0-javadoc.jar (classifier = javadoc) mylib-1.0-tests.jar (classifier = tests) ``` ## Where the classifier comes from For a `Jar` task, the file name's classifier segment is `archiveClassifier`. The main artifact leaves it empty; each secondary jar sets a unique value: ```kotlin tasks.register<Jar>("sourcesJar") { archiveClassifier.set("sources") from(sourceSets.main.get().allSource) } tasks.register<Jar>("javadocJar") { archiveClassifier.set("javadoc") from(tasks.named("javadoc")) } ``` (The `java` plugin's `withSourcesJar()` / `withJavadocJar()` do exactly this for you.) ## What collisions break If two artifacts in the same publication share the same classifier+extension: - **Validation failure**: `maven-publish` detects a duplicate artifact and fails the build with a clear error. - **Silent overwrite**: if added in a way that bypasses validation, one file's bytes land at the same repository path and the other is lost — consumers requesting `:sources` may get the wrong content. - **Consumer confusion**: tools resolve artifacts by classifier; an ambiguous classifier means the IDE can't fetch sources or javadoc reliably. ## Consistency between task and publication The classifier embedded in the file name (`archiveClassifier`) should match the classifier the publication advertises. The clean approach is to register the jar with the correct `archiveClassifier` and let the component/publication infer the classifier from the artifact's task, rather than hardcoding a different classifier on the `artifact(...)` entry — mismatches lead to a file named one way but published under another classifier. ## Why this matters Sources and javadoc jars are core to a good developer experience: IDEs download them by classifier to show source and docs. Getting classifiers unique and consistent is what makes that work and keeps the publication valid.

  • What shortcut does the java plugin give for sources and javadoc jars?
    `java { withSourcesJar(); withJavadocJar() }` registers the tasks with the correct classifiers and wires them into the main component/publication.
  • What happens if you give two jars the classifier 'sources'?
    The publication has two artifacts with the same classifier+extension under one GAV, which is a collision — maven-publish fails validation or one overwrites the other.

saying these in an interview costs you the question

  • Saying different jars need different versions to coexist (they share the version; classifier disambiguates).
  • Believing two jars can share a classifier as long as their contents differ.
  • Setting archiveClassifier on the task but a different classifier on the artifact() entry.

context