Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clarify types, extensions, and classifiers #695

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
161 changes: 98 additions & 63 deletions content/markdown/repositories/artifacts.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ issues without you noticing it. In short, these cases should be avoided.

## Artifact Properties

The artifacts that Maven uses internally have the following properties:
The artifacts that Maven uses have the following coordinate properties:

| Name | Description |
|-------------|-------------------------------------------------|
Expand All @@ -55,50 +55,78 @@ snapshot version "1.0-20220119.164608-1" has the `baseVersion` "1.0-SNAPSHOT".
So, `version` and `baseVersion` are linked, derived from each other, but **they have different values only in the
case of snapshots**.

## But where do I set the Artifact extension?

In short, nowhere. Or maybe "you rarely have to". The Maven POM (where you declare your project, parent project,
dependencies, plugins and other items), maps those elements onto artifact extensions with some extra logic.

In case of "project" and "parent project" POMs (after the POM is made into an effective POM, that is, parent values have been inherited):

| Artifact Property | Project POM (pom.xml) | POM Artifact |
|-------------------|-----------------------|----------------|
| groupId | `project/groupId` | -> groupId |
| artifactId | `project/artifactId` | -> artifactId |
| version | `project/version` | -> version |
| classifier | - | "" (always) |
| extension | - | `pom` (always) |

In the case of "build plugins" and "build extensions", as they are JARs, this is how corresponding elements are mapped
(for build extension change the XML path prefix to `project/build/extensions/extension[x]`):

| Artifact Property | Plugin in Project POM | Plugin/Extension Artifact |
|-------------------|----------------------------------------------|---------------------------|
| groupId | `project/build/plugins/plugin[x]/groupId` | -> groupId |
| artifactId | `project/build/plugins/plugin[x]/artifactId` | -> artifactId |
| version | `project/build/plugins/plugin[x]/version` | -> version |
| classifier | - | -> "" (always) |
| extension | - | -> `jar` (always) |

And finally, in the case of "dependencies", this is the mapping (no, scope is NOT part of artifact coordinates):

| Artifact Property | Dependency in Project POM | Dependency Artifact |
|-------------------|-------------------------------------------------|-------------------------------------------|
| groupId | `project/dependencies/dependency[x]/groupId` | -> groupId |
| artifactId | `project/dependencies/dependency[x]/artifactId` | -> artifactId |
| version | `project/dependencies/dependency[x]/version` | -> version |
| classifier | `project/dependencies/dependency[x]/classifier` | -> classifier |
| extension | `project/dependencies/dependency[x]/type` | -> type handler provided, or same as type |

Here, we need to make a short detour to explain "dependency type" and how it becomes an artifact extension.

A dependency type determines how the artifact referenced by the dependency is used.
For example, should it be added to comple-time classpath, the test classpath, or both?
Plugins and extensions may define new types. This is usually required for plugins introducing
a "packaging" (lifecycle mapping) by providing `ArtifactHandler` components with a name corresponding to type name.

Out of the box, Maven Core defines the [following "types" (handled by the same named `ArtifactHandler` components)](/ref/current/maven-core/artifact-handlers.html):
Maven pom.xml files identify artifacts via coordinates in four different ways, depending
on what the artifact is and how it will be used.

* A dependency of the project, often though not always a jar archive, is referenced by
a `dependency` element in either the `dependencies` or `dependenciesManagement` section.
* The pom.xml file itself has coordinates given by the top-level `groupId`,
`artifactId`, and `version` elements.
* A build plugin is referenced by a `plugin` element in the `plugins` section.
* A build extension is referenced by an `extension` element in the `extensions` section.

For POM artifacts that contain a project's pom.xml file, the artifact coordinates are set
as follows:

| Artifact Coordinate | POM element | Coordinate Value |
|---------------------|----------------------|-------------------|
| groupId | `project/groupId` | -> group ID |
| artifactId | `project/artifactId` | -> artifact ID |
| version | `project/version` | -> version string |
| classifier | - | "" (always) |
| extension | - | "pom" (always) |

Coordinate values are computed after the POM is made into an effective POM;
that is, after parent values have been inherited.

Build plugin and build extension artifacts are JARs. For build plugins,
this is how the corresponding coordinates are computed from a `plugin` element:

| Artifact Coordinate | POM element | Coordinate Value |
|---------------------|-------------------------------------------|-------------------|
| groupId | `project/build/plugins/plugin/groupId` | -> group ID |
| artifactId | `project/build/plugins/plugin/artifactId` | -> artifact ID |
| version | `project/build/plugins/plugin/version` | -> version string |
| classifier | - | -> "" (always) |
| extension | - | -> "jar" (always) |

Build extensions are similarly computed from an `extension` element (which is not the
same as and should not be confused with the extension artifact coordinate):

| Artifact Coordinate | POM element | Coordinate Value |
|---------------------|-------------------------------------------------|-------------------|
| groupId | `project/build/extensions/extension/groupId` | -> group ID |
| artifactId | `project/build/extensions/extension/artifactId` | -> artifact ID |
| version | `project/build/extensions/extension/version` | -> version string |
| classifier | - | -> "" (always) |
| extension | - | -> "jar" (always) |

Note: The *extension artifact coordinate* and a *Maven build extension* are two completely different
things that unfortunately share the name "extension". A
[Maven build extension](https://maven.apache.org/guides/mini/guide-using-extensions.html) is
a JAR archive that is added to the project class loader's classpath.
It is referenced by an `extension` element in pom.xml. An extension coordinate
is usually the filename extension of an artifact's JAR file such as jar, zip, or txt.
This extension is most often set to an implicit default value,
but can be changed by the `type` child of a `dependency` element.

Finally, in the case of "dependencies", this is how artifact coordinates are calculated
from a `dependency` element:

| Artifact Coordinate | POM element | Coordinate Value |
|---------------------|----------------------------------------------|-------------------------------------------|
| groupId | `project/dependencies/dependency/groupId` | -> group ID |
| artifactId | `project/dependencies/dependency/artifactId` | -> artifact ID |
| version | `project/dependencies/dependency/version` | -> version string |
| classifier | `project/dependencies/dependency/classifier` | -> classifier, or type handler provided |
| extension | `project/dependencies/dependency/type` | -> type handler provided, or same as type |

This also applies when the `dependency` element is a child of a `dependencyManagement` element.

Notice that the `dependency` element does not have an `extension` element.
Instead it has a `type` element which is
used to derive the extension and sometimes the classifier.
Out of the box, Maven Core defines 11 "types" [(handled by the same named `ArtifactHandler` components)](/ref/current/maven-core/artifact-handlers.html):

| Type Name | Extension | Classifier |
|--------------|-----------|--------------|
Expand All @@ -115,16 +143,19 @@ Out of the box, Maven Core defines the [following "types" (handled by the same n
| war | `war` | |
| **any** | any | |

From the table above, we can see that if we define the dependency type as "war", we will hit the "war" handler. That will
result in using the `war` extension (which may not be obvious, as the type and extension we end up with are the same, but internally this
indirection does happen). The "test-jar" is more obvious, as it translates to the `jar` extension. Finally, the **any**
last row will be used if none of the above match. Hence in that case the "type" is used as the "extension". For example.
if the dependency type is `<type>tar.gz</type>`, the extension will also be `tar.gz`.
This table may be extended by plugins and extensions used in the build.
From the table above, you can see that if the dependency type is "war",
the extension is also `war` and the classifier is the value of the
`classifier` element (if present) or the empty string if the `classifier` element
is not present. If the type is "test-jar", the extension is
"jar" and the classifier is "tests". If the type is not one of these 11 names, then the
value of the "type" is used as the extension. For example, if the `type` element
is `<type>tar.gz</type>`, the extension will be `tar.gz`, and the classifier will
be set by the `classifier` element. This
table may be extended by plugins and extensions used in the build.

Also, this has "interesting" consequences. Consider the artifact
`org.project:reusable-test-support:1.0:tests:jar`. With the type handlers above, maybe surprisingly, the dependency to
this very same artifact can be described in two ways:
This has "interesting" consequences. Consider the artifact
`org.project:reusable-test-support:1.0:tests:jar`. Maybe surprisingly,
a dependency on this artifact can be described in two ways:

```xml
<dependency>
Expand All @@ -146,16 +177,20 @@ and the equivalent dependency would be:
</dependency>
```

The obvious difference is presence of `classifier` in first case, while in second lack of it but presence of `type` "test-jar",
that in the other hand, implies a classifier of "tests". In both cases, the extension is "jar". The first it uses the default value for this property, while the second type defines it.
The obvious difference is that the first example has the `<classifier>tests</classifier>`,
while the second has the `<type>test-jar</type>`. The `type` "test-jar"
implies a classifier of "tests". In both cases, the extension is "jar".
The first uses the default value for the extension, while the second
derives it from the type.

Note: In this very case, the first way is somewhat "explicit", and is recommended. Not so for the
cases when type handler carries some important extra information (like some custom packaging), where using `type`
is more appropriate. Simply put, in this case the type "test-jar" is like an alias for ordinary JARs with the "tests"
classifier.
Note: In this case, the first way is somewhat "explicit", and is
recommended. When the type handler carries important
extra information such as custom packaging, using `type` is more
appropriate. Simply put, in this example the type "test-jar" is like an
alias for ordinary JARs with the "tests" classifier.

## Summary

In short, this is how various Maven bits like "project", "parent project", "plugin", "extension" and "dependency"
have artifact coordinates mapped from POM elements. Using this knowledge, we can always deduce the artifact coordinate
of these POM elements.
In short, this is how various Maven bits like "project", "parent
project", "plugin", "extension", and "dependency" derive artifact
coordinates from POM elements.