JAR File Identification

Complex Java applications usually consist of multiple components. Each component can have multiple implementations, called artifacts. Artifacts in Java context are usually JAR files, but can also be WAR files or any other kind of file.

There are multiple incompatible ways of identifying (naming) Java artifacts and each build system often encourages usage of specific naming scheme. This means that Linux distributions also need to allow each artifact to be located using several different identifiers, possible using different schemes. On the other hand it is virtually impossible to every naming scheme, so there are some simplifications.

This chapter describes artifact different ways to identify and locate artifacts in system repository.

Relative paths

JAR artifacts are installed in one of standard directory trees. Usually this is either %{_javadir} (/usr/share/java) or %{_jnidir} (/usr/lib/java).

The simplest way of identifying artifacts is using their relative path from one of standard locations. All artifact can be identified this way because each artifacts has a unique file name. Each path identifying artifact will be called artifact path in this document.

To keep artifact paths simpler and more readable, extension can be omitted if it is equal to jar. For non-JAR artifacts extension cannot be omitted and must be retained.

Additionally, if artifact path points to a directory then it represents all artifacts contained in this directory. This allows a whole set of related artifacts to be referenced easily by specifying directory name containing all of them.

If the same artifact path has valid expansions in two different root directories then it is unspecified which artifacts will be located.

Artifact specification

As noted in previous section, every artifact can be uniquely identified by its file path. However this is not always the preferred way of artifact identification.

Modern Java build systems provide a way of identifying artifacts with an abstract identifier, or more often a pair of identifiers. The first if usually called group ID or organization ID while the second is just artifact ID. This pair of identifiers will be called artifact coordinates in this document. Besides group ID and artifact ID, artifact coordinates may also include other (optional) information about artifact, such as extension, classifier or version.

In Linux distributions it’s important to stay close to upstreams providing software being packaged, so the ability to identify artifacts in the same way as upstream does is very important from the point of view of packaging. Every artifact can optionally be identified by artifact coordinates assigned during package build. Packages built with Maven automatically use this feature, but all other packages, even these built with pure javac, can use this feature too (see description of %mvn_artifact and %add_maven_depmap macros).

Aliases

Aliases working in two ways:

  • Symlinks for paths

  • Additional mappings for artifact specifications

In the real world the same project can appear under different names as it was evolving or released differently. Therefore other projects may refer to those alternative names instead of using the name currently prefered by upstream.

Artifact aliases

XMvn provides a way to attach multiple artifact coordinates to a single artifact. Dependent projects that use alternative coordinates can then be built without the need to patch their POMs or alter the build by other means. It will also generate virtual provides for the alias, so it can be also used in Requires and BuildRequires. Creating an alias is achieved by %mvn_alias macro.

Example invocation
# com.example.foo:bar (the actual artifact existing in the project) will also
# be available as com.example.foo:bar-all
%mvn_alias com.example.foo:bar com.example.foo:bar-all

# You don't need to repeat the part of coordinates that stays the same
# (groupID in this case)
%mvn_alias com.example.foo:bar :bar-all

# You can specify multiple aliases at once
%mvn_alias com.example.foo:bar :bar-all :bar-lib

# The macro supports several shortcuts to generate multiple alisaes.
# Braces - {} - capture their content, which can then be referenced in the
# alias part with @N, where N is the index of the capture group.
# * acts as a wildcard (matching anything)
# The following generates aliases ending with shaded for all artifacts in the
# project
%mvn_alias 'com.example.foo:{*}' :@1-shaded

Compatibility versions

Handling of compatibility packages, versioned jars etc.

In Fedora we prefer to always have only the latest version of a given project. Unfortunately, this is not always possible as some projects change too much and it would be too hard to port dependent packages to the current version. It’s not possible to just update the package and keep the old version around as the names, file paths and dependency provides would clash. The recommended practice is to update the current package to the new version and create new package representing the old version (called compat package). The compat package needs to have the version number (usually only the major number, unless further distinction is necessary) appended to the name, thus effectivelly having different name from RPM’s point of view. Such compat package needs to perform some additional steps to ensure that it can be installed and used along the non-compat one.

You should always evaluate whether creating a compat package is really necessary. Porting dependent projects to new versions of dependencies may be a compicated task, but your effort would be appreciated and it’s likely that the patch will be accepted upstream at some point in time. If the upstream is already inactive and the package is not required by anything, you should also consider retiring it.

Maven Compat Versions

XMvn supports marking particular artifact as compat, performing the necessary steps to avoid clashes with the non-compat version. An artifact can be marked as compat by %mvn_compat_version. It accepts an artifact argument which will determine which artifact will be compat. The format for specifying artifact coordinates is the same as with %mvn_alias. In the common case you will want to mark all artifacts as compat. You can specify multiple compat versions at a time.

Dependency resolution of compat artifacts

When XMvn performs dependency resolution for a dependency artifact in a project, it checks the dependency version and compares it against all versions of the artifact installed in the buildroot. If none of the compat artifacts matches it will resolve the artifact to the non-compat one. This has a few implications:

  • The versions are compared for exact match. The compat package should provide all applicable versions that are present in packages that are supposed to be used with this version.

  • The dependent packages need to have correct BuildRequires on the compat package as the virtual provides is also different (see below).

File names and virtual provides

In order to prevent file name clashes, compat artifacts have the first specified compat version appended to the filename. Virtual provides for compat artifacts also contain the version as the last part of the coordinates. There are multiple provides for each specified compat version. Non-compat artifact don’t have any version in the virtual provides.

Example invocation of %mvn_compat_version
# Assuming the package has name bar and version 3
# Sets the compat version of foo:bar artifact to 3
%mvn_compat_version foo:bar 3
# The installed artifact file (assuming it's jar and there were no
# %mvn_file calls) will be at %{_javadir}/bar/bar-3.jar
# The generated provides for foo:bar will be
# mvn(foo:bar:3) = 3
# mvn(foo:bar:pom:3) = 3

# Sets the compat versions of all artifacts in the build to 3 and 3.2
%mvn_compat_version : 3 3.2