Affected version
Maven Resolver 2.0.x — the default changed in the 2.0 line and is still present in the latest
release 2.0.21 (verified 2.0.13, 2.0.20, 2.0.21). Resolver 1.9.x is unaffected. Surfaced via
Maven 3.10.0-rc-1 (bundles 2.0.20) and Maven 4.0.0-rc-5 (bundles 2.0.13); the current Maven
master bundles 2.0.21, maven-4.0.x bundles 2.0.20. Next resolver release: 2.0.22.
Bug description
Summary
Since Resolver 2.0, the default named-lock factory is file-lock
(DefaultNamedLockFactorySelector.DEFAULT_FACTORY_NAME = FileLockNamedLockFactory.NAME),
whereas 1.9.x effectively used in-memory locks. file-lock writes a .locks directory
inside the local repository base directory (BasedirNameMapper.DEFAULT_LOCKS_DIR = ".locks").
Two consequences follow for any consumer (e.g. Maven), on a plain resolve/install:
- A
.locks directory now appears in the local repository on every OS (empty on
POSIX, populated on Windows).
- On Windows the 0-byte lock files are not cleaned up and remain. When the local
repository is also used as a deploy/staging tree (as Sonatype's third-party
central-publishing-maven-plugin does under target/), those .locks files get bundled
and the Central Portal rejects the bundle (a .locks entry has no .pom).
With resolver 1.9.x (Maven 3.9.x) no lock files were produced at all. So this is a behavior
change introduced by the 2.0 default.
Reported downstream on users@maven.apache.org:
https://lists.apache.org/thread/1trdtdct2glycrzqo6xhbryns5o5pxno
Root cause
1. file-lock is the 2.0 default; the .locks dir lives inside the local repo.
maven-resolver-impl/.../internal/impl/named/DefaultNamedLockFactorySelector.java:
public static final String DEFAULT_FACTORY_NAME = FileLockNamedLockFactory.NAME; // "file-lock"
maven-resolver-impl/.../internal/impl/synccontext/named/BasedirNameMapper.java:
public static final String DEFAULT_LOCKS_DIR = ".locks";
The base directory defaults to the local repository root, so the lock files are written into
the very tree that consumers may treat as content. DefaultNamedLockFactorySelector did not
exist in 1.9.x, whose default was in-memory — consistent with 1.9.x producing no .locks.
Note: Maven's shipped distribution does not override this back — its
apache-maven/src/assembly/maven/conf/maven-system.properties sets no
aether.syncContext.named.factory on master, maven-4.0.x or 3.10.x. (A rwlock-local
override exists only as a maven-cli test fixture, not in the distribution.) So the resolver
default is what users get.
2. The files are left behind only on Windows.
maven-resolver-named-locks/.../providers/FileLockNamedLockFactory.java:
private static final boolean IS_WINDOWS =
System.getProperty("os.name", "unknown").startsWith("Windows");
private static final boolean DELETE_LOCK_FILES =
Boolean.parseBoolean(System.getProperty(
"aether.named.file-lock.deleteLockFiles", Boolean.toString(!IS_WINDOWS)));
- Linux/macOS: the lock file is opened with
StandardOpenOption.DELETE_ON_CLOSE and
removed on close — the .locks directory ends up empty.
- Windows:
DELETE_LOCK_FILES defaults to false (a deliberate tweak, since
DELETE_ON_CLOSE causes concurrency issues on Windows) → the 0-byte .locks files remain.
So the default change (.locks created inside the local repo) is OS-independent; the
leftover files that break deploy staging are the Windows-specific consequence.
Verification (end-to-end, no third-party plugin)
A plain mvn install in default configuration into an isolated local repository creates the
.locks directory — proving it is the resolver default at work, no plugin involved. A core IT
(MavenITghFileLockDefaultLocksTest) asserts a default run creates no .locks directory;
it fails on every released Maven that bundles resolver 2.0. CI matrix — all six jobs fail at
the assertion:
|
ubuntu |
windows |
macOS |
| Maven 3.10.0-rc-1 (resolver 2.0.20) |
Failures: 1 |
Failures: 1 |
Failures: 1 |
| Maven 4.0.0-rc-5 (resolver 2.0.13) |
Failures: 1 |
Failures: 1 |
Failures: 1 |
CI run: https://github.com/aschemaven/maven-integration-testing/actions/runs/30391734694
The Windows leftover files also reproduce on Linux/macOS by forcing the Windows path with
-Daether.named.file-lock.deleteLockFiles=false (168 leftover 0-byte files in a small build).
Workaround for consumers
-Daether.syncContext.named.factory=rwlock-local
Suggested direction (maintainers decide)
Options, roughly in order of "fixes the root cause":
- Do not place
file-lock lock files inside the local repository — use a location that
is never treated as content (e.g. a temp/state dir), so consumers can't bundle them.
- Clean up the lock files on Windows too (or make
.locks never contain deployable-looking
entries).
- Reconsider
file-lock as the default for the common single-process case.
(A consumer-side mitigation — Maven defaulting aether.syncContext.named.factory=rwlock-local
— treats the symptom, not the cause.)
Environment
- Maven Resolver
2.0.13, 2.0.20, 2.0.21 (affected — DEFAULT_FACTORY_NAME is still
file-lock in 2.0.21); 1.9.27 unaffected. Next release: 2.0.22.
- Consumers observed: Maven
3.10.0-rc-1, 4.0.0-rc-5, current maven-4.0.x (resolver 2.0.20)
and master (resolver 2.0.21).
- OS:
.locks directory created on all of Linux/macOS/Windows; leftover .locks files on
Windows (the fatal case for deploy staging).
- Downstream where it surfaced:
org.sonatype.central:central-publishing-maven-plugin:0.11.0
publishing to central.sonatype.com.
Affected version
Maven Resolver 2.0.x — the default changed in the 2.0 line and is still present in the latest
release
2.0.21(verified 2.0.13, 2.0.20, 2.0.21). Resolver 1.9.x is unaffected. Surfaced viaMaven 3.10.0-rc-1 (bundles 2.0.20) and Maven 4.0.0-rc-5 (bundles 2.0.13); the current Maven
masterbundles2.0.21,maven-4.0.xbundles2.0.20. Next resolver release:2.0.22.Bug description
Summary
Since Resolver 2.0, the default named-lock factory is
file-lock(
DefaultNamedLockFactorySelector.DEFAULT_FACTORY_NAME = FileLockNamedLockFactory.NAME),whereas 1.9.x effectively used in-memory locks.
file-lockwrites a.locksdirectoryinside the local repository base directory (
BasedirNameMapper.DEFAULT_LOCKS_DIR = ".locks").Two consequences follow for any consumer (e.g. Maven), on a plain resolve/install:
.locksdirectory now appears in the local repository on every OS (empty onPOSIX, populated on Windows).
repository is also used as a deploy/staging tree (as Sonatype's third-party
central-publishing-maven-plugindoes undertarget/), those.locksfiles get bundledand the Central Portal rejects the bundle (a
.locksentry has no.pom).With resolver 1.9.x (Maven 3.9.x) no lock files were produced at all. So this is a behavior
change introduced by the 2.0 default.
Reported downstream on users@maven.apache.org:
https://lists.apache.org/thread/1trdtdct2glycrzqo6xhbryns5o5pxno
Root cause
1.
file-lockis the 2.0 default; the.locksdir lives inside the local repo.maven-resolver-impl/.../internal/impl/named/DefaultNamedLockFactorySelector.java:maven-resolver-impl/.../internal/impl/synccontext/named/BasedirNameMapper.java:The base directory defaults to the local repository root, so the lock files are written into
the very tree that consumers may treat as content.
DefaultNamedLockFactorySelectordid notexist in 1.9.x, whose default was in-memory — consistent with 1.9.x producing no
.locks.Note: Maven's shipped distribution does not override this back — its
apache-maven/src/assembly/maven/conf/maven-system.propertiessets noaether.syncContext.named.factoryonmaster,maven-4.0.xor 3.10.x. (Arwlock-localoverride exists only as a maven-cli test fixture, not in the distribution.) So the resolver
default is what users get.
2. The files are left behind only on Windows.
maven-resolver-named-locks/.../providers/FileLockNamedLockFactory.java:StandardOpenOption.DELETE_ON_CLOSEandremoved on close — the
.locksdirectory ends up empty.DELETE_LOCK_FILESdefaults tofalse(a deliberate tweak, sinceDELETE_ON_CLOSEcauses concurrency issues on Windows) → the 0-byte.locksfiles remain.So the default change (
.lockscreated inside the local repo) is OS-independent; theleftover files that break deploy staging are the Windows-specific consequence.
Verification (end-to-end, no third-party plugin)
A plain
mvn installin default configuration into an isolated local repository creates the.locksdirectory — proving it is the resolver default at work, no plugin involved. A core IT(
MavenITghFileLockDefaultLocksTest) asserts a default run creates no.locksdirectory;it fails on every released Maven that bundles resolver 2.0. CI matrix — all six jobs fail at
the assertion:
CI run: https://github.com/aschemaven/maven-integration-testing/actions/runs/30391734694
The Windows leftover files also reproduce on Linux/macOS by forcing the Windows path with
-Daether.named.file-lock.deleteLockFiles=false(168 leftover 0-byte files in a small build).Workaround for consumers
Suggested direction (maintainers decide)
Options, roughly in order of "fixes the root cause":
file-locklock files inside the local repository — use a location thatis never treated as content (e.g. a temp/state dir), so consumers can't bundle them.
.locksnever contain deployable-lookingentries).
file-lockas the default for the common single-process case.(A consumer-side mitigation — Maven defaulting
aether.syncContext.named.factory=rwlock-local— treats the symptom, not the cause.)
Environment
2.0.13,2.0.20,2.0.21(affected —DEFAULT_FACTORY_NAMEis stillfile-lockin 2.0.21);1.9.27unaffected. Next release:2.0.22.3.10.0-rc-1,4.0.0-rc-5, currentmaven-4.0.x(resolver 2.0.20)and
master(resolver 2.0.21)..locksdirectory created on all of Linux/macOS/Windows; leftover.locksfiles onWindows (the fatal case for deploy staging).
org.sonatype.central:central-publishing-maven-plugin:0.11.0publishing to
central.sonatype.com.