Local Repository
Maven Resolver implements a “local repository” (that is used by Maven itself as well), that since the beginning of time was a “mixed bag of beans”, it served twofold purposes: to cache the artifacts downloaded from remote, but also to store the artifacts locally installed (locally built and installed, to be more precise). Both of these artifacts were stored in bulk in the local repository.
Implementations
Local repository implementations implement the LocalRepositoryManager
(LRM)
interface, and Resolver out of the box provides two implementations for it:
“enhanced” used at runtime and “simple” meant to be used in tests and alike
scenarios (is not meant for production use).
Enhanced LRM
Enhanced LRM is enhanced with several extra features, one most notable is scoping cached content by its origin and context: if you downloaded an artifact A1 from repository R1 and later initiate build that requires same artifact A1, but repository R1 is not defined in build, the A1 artifact cached from R1 remote repository should be handled as not present, and needs to be re-downloaded, despite same coordinates. Those two, originating from two different repositories may not be the same thing. This is meant to protect users from “bad practice” (artifact coordinates are unique in ideal world).
Split Local Repository
Latest addition to the enhanced LRM is split feature. By default, split feature is not enabled, enhanced LRM behaves as it behaved in all previous versions of Resolver.
Enhanced LRM is able to split the content of local repository by several conditions:
- differentiate between cached and locally installed artifacts
- differentiate cached artifacts based on their origin (remote repository)
- differentiate between release and snapshot versioned artifacts
The split feature is implemented by the LocalPathPrefixComposer
interface,
that adds different “prefixes” for the locally stored artifacts, based on
their context.
Note About Release And Snapshot Differentiation
The prefix composer is able to differentiate between release and snapshot versioned artifacts, and this is clear-cut: Maven Artifacts are either this or that.
On the other hand, in case of Maven Metadata, things are not so clear.
Resolver is able to differentiate only based on metadata/version
field, but that field is not always present.
Maven Metadata exists in 3 variants:
- G level metadata does not carry version.
- GA level metadata does not carry version.
- GAV level metadata carries version.
In short, G and GA level metadata always end up in releases, despite
GA level metadata body may contain mixed release and snapshot versions enlisted,
as this metadata does not contain the metadata/version
field.
The GAV level metadata gets differentiated based on version it carries, so
they may end up in releases or snapshots, depending on their value of
metadata/version
field.
Use Cases
Most direct use case is simpler local repository eviction. One can delete all locally built artifacts without deleting the cached ones, hence, no need to re-download the “whole universe”. Similarly, deletion of cached ones can happen based even on origin repository (if split by remote repository was enabled beforehand).
Example configuration with split by remote repository:
$ mvn ... -Daether.enhancedLocalRepository.split \
-Daether.enhancedLocalRepository.splitRemoteRepository
Another use case is interesting for “branched development”. Before split feature,
a developer simultaneously working on several branches of same project was forced
to rebuild all (better: install all), as same built artifacts from different
branches would still land in the local repository on same coordinates, hence, they
would constantly overwrite each other. It was easy to get into false error
state where partially overlapping content were present in the local repository from
different branches. Now one can set unique local prefix for each
branch it is working on (or even by project, like
-Daether.enhancedLocalRepository.localPrefix=$PROJECT/$BRANCH
, but use
actual values, these expressions are merely an example, there is no interpolation
happening!) and the
local repository becomes usable even simultaneously, even concurrently from
different terminals, as different projects and their branches can simply
coexist in local repository. They will land in different places, due different
prefixes.
Example configuration for branches:
$ mvn ... -Daether.enhancedLocalRepository.split \
-Daether.enhancedLocalRepository.localPrefix=maven-resolver/mresolver-253
-Daether.enhancedLocalRepository.splitRemoteRepository
For complete reference of enhanced LRM configuration possibilities, refer to configuration page.
Split Repository Considerations
Word of warning: on every change of “split” parameters, user must be aware
of the consequences. For example, if one change all aspects of split
configuration (all the prefixes), it may be considered logically equivalent
of defining a new local repository, despite local repository root (-Dmaven.repo.local
)
is unchanged! Simply put, as all prefixes will be “new”, the composed paths will
point to potentially non-existing locations, hence, resolver will consider
it as a “new” local repository in every aspect.
Implementing Custom Split Strategy
To implement custom split strategy, one needs to create a component of
type LocalPathPrefixComposerFactory
and override the default component
offered by Resolver (for example by using Eclipse Sisu priorities for
components). This should be done by extending LocalPathPrefixComposerFactorySupport
class that provides all the defaults.
The factory should create a stateless instance of a composer configured from passed in session, that will be used with the enhanced LRM throughout the session.
Simple LRM
Simple is a fully functional LRM implementation, but is used mainly in tests, it is not recommended in production environments.
To manually instantiate a simple LRM, one needs to invoke following code:
LocalRepositoryManager simple = new SimpleLocalRepositoryManagerFactory()
.newInstance( session, new LocalRepository( basedir ) );
Note: This code snippet above instantiates a component, that is not recommended way to use it, as it should be rather injected whenever possible. This example above is merely a showcase how to obtain LRM implementation in unit tests.
Shared Access to Local Repository
In case of shared (multi-threaded, multi-process or even multi host) access to local repository, coordination is a must, as local repository is hosted on file system, and each thread may read and write concurrently into it, causing other threads or processes to get incomplete or partially written data.
Hence, since Resolver 1.7.x version, there is a pluggable API called “Named Locks” available, providing out of the box lock implementations for cases like:
- multi-threaded, in JVM locking (the default)
- multi-process locking using file system advisory locking
- multi-host locking using Hazelcast or Redisson (needs Hazelcast or Redisson cluster)
For details see Named Locks module.