Skip to content

FELIX-6862 Only handle configuration files this installer handles - #562

Open
jkevan wants to merge 2 commits into
apache:masterfrom
Jahia:felix-5832-complete-the-ownership-filter
Open

jkevan wants to merge 2 commits into
apache:masterfrom
Jahia:felix-5832-complete-the-ownership-filter

Conversation

@jkevan

@jkevan jkevan commented Sep 15, 2026

Copy link
Copy Markdown

Fixes FELIX-6862.

What happens

ConfigInstaller adopts every configuration that records felix.fileinstall.filename, whatever the format of the file. The CM_DELETED path then deletes the file that the resulting pidToFile entry names. An installer therefore deletes a configuration file that another ArtifactInstaller handles.

Why

FELIX-5832 added a canHandle filter to the CM_UPDATED path in 2018, under the rule its subject states: only handle ConfigurationEvent objects managed by us. Three sites read or write pidToFile, and that change covered one of them.

  • init() puts every configuration that records a file name into pidToFile, with no filter.
  • The CM_DELETED path removes the pid from pidToFile and deletes the file, with no filter.

The defect needs a restart, because init() is the only unfiltered writer of pidToFile. The guarded CM_UPDATED path rejects a foreign format, so a configuration created while the container runs never enters the map.

What changed

init() applies the filter, so a file of another format no longer enters pidToFile. The CM_DELETED path applies it too, because deleting the file is the act that loses data. A filter at each writer alone makes the invariant depend on every future writer repeating it.

init() passes new File(fileName) rather than fromConfigKey(fileName). canHandle reads the file name only, and fromConfigKey calls URI.create, which throws on a value that is not a URI. The catch around that loop would then leave pidToFile half-built.

Behaviour for .cfg and .config files is unchanged.

pidToFile is package-private, so that a test can read what init() adopts. ConfigInstaller already exposes shouldSaveConfig and getConfiguration to ConfigInstallerTest for the same reason.

Tests

Two tests read the behaviour through the file.

  • testCmDeletedKeepsAFileOfAnotherInstallersFormat creates a .yml file, runs init(), raises CM_DELETED, and asserts the file survives. The test fails when both filters are removed.
  • testCmDeletedStillRemovesAFileOfItsOwnFormat does the same with a .cfg file and asserts the file is deleted. The result is the same with and without the filters, so the test guards the unchanged path.

Either filter keeps the .yml file alive on its own, so the two tests above pin the behaviour rather than each filter. Three more tests read pidToFile, and each one fails on one site alone.

  • testInitLeavesAConfigurationOfAnotherInstallersFormatAlone runs init() over a configuration that records a .yml file, and asserts the map stays empty.
  • testInitAdoptsAConfigurationOfItsOwnFormat does the same with a .cfg file, and asserts the map names it.
  • testCmDeletedKeepsAFileTheMapNamesAndThisInstallerDoesNotHandle puts a .yml file in the map itself, which is the state the deletion site is there for, and raises CM_DELETED.

The comment at each filter states why that filter is there, and the comment on the field states why the map is not private.

mvn test on the fileinstall module passes on JDK 11, except DirectoryWatcherTest.testInvalidTempDir. That test also fails on an unmodified master on this machine, which is FELIX-6837.

FELIX-5832 added a canHandle filter to the CM_UPDATED path, under the rule its
subject states: only handle ConfigurationEvents for config objects managed by
us. Three sites read or write pidToFile, and that change covered one of them.

init() adopts every configuration that records felix.fileinstall.filename,
whatever the format of the file. The CM_DELETED path then deletes the file that
the resulting entry names. Another ArtifactInstaller records the same property
for a format of its own, so after a restart this installer owns that installer's
files and deletes one when its configuration is deleted.

init() applies the filter now, so a file of another format no longer enters
pidToFile. The CM_DELETED path applies it too, because deleting the file is the
act that loses data and a filter at each writer makes the invariant depend on
every future writer repeating it.

canHandle reads the file name only, so new File is enough in init(). Passing
fromConfigKey would call URI.create, which throws on a value that is not a URI,
and the catch around that loop would then leave pidToFile half-built.

Behaviour for .cfg and .config files is unchanged. Two tests cover the pair, and
the first fails when both filters are removed.
A reviewer of the same change on a fork measured what the suite holds. Removing
the init() filter alone left the suite green, removing the CM_DELETED filter
alone left it green, and only removing both failed a test. Either filter keeps a
foreign file alive, so a test that reads the file cannot fail on one site alone.

pidToFile is package-private now, and three tests read it. ConfigInstaller
already exposes shouldSaveConfig and getConfiguration to this test class for the
same reason, and the comment on the field states why the map is not private.

testInitLeavesAConfigurationOfAnotherInstallersFormatAlone runs init() over a
configuration that records a .yml file, and asserts the map stays empty.
testInitAdoptsAConfigurationOfItsOwnFormat does the same with a .cfg file and
asserts the map names it. A third test puts a .yml file in the map itself, which
is the state the deletion site is there for, and raises CM_DELETED. Each of the
three fails when its own filter goes away.

The two tests that read the file stay, because they state the behaviour a user
sees.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant