allthingssecurity opened a new pull request, #28640:
URL: https://github.com/apache/camel/pull/28640

   # Description
   
   [CAMEL-26287](https://issues.apache.org/jira/browse/CAMEL-26287)
   
   `JCacheEndpoint.doStop` calls `JCacheManager.close()`, which closed the 
cache, the cache manager and the caching provider of the endpoint. The provider 
comes from `Caching.getCachingProvider()`, a JVM singleton, and the JCache API 
gives the same cache manager for the same uri and the same cache for the same 
name, so all jcache endpoints of the JVM (and the jcache idempotent, 
aggregation and key-value repositories, which use `JCacheManager` too) share 
them, and closing the provider closes all its cache managers and caches. An 
endpoint is stopped when the only route that uses it is removed (a reload, a 
route template removed) and when its CamelContext stops. Afterwards every 
operation of every other jcache endpoint failed with `IllegalStateException: 
Cache operations can not be performed. The cache closed` (Hazelcast provider), 
for good, as `JCacheManager` kept the closed cache:
   - two routes `to("jcache://orders")` and `to("jcache://sessions")`: removing 
the second breaks the first;
   - a consumer route `from("jcache://orders?filteredEvents=...")` and a 
producer route `to("jcache://orders")` (two endpoints, one cache): removing the 
consumer route breaks the producer;
   - two CamelContexts in one JVM: stopping one breaks the jcache endpoints of 
the other (read, not run).
   
   Changes:
   - `JCacheManager.getCache()` counts (JVM-wide, a static map under a lock) 
the managers that use each provider, cache manager and cache it got; the cache 
is counted by its cache manager and name.
   - `JCacheManager.close()` closes each of them only when it is the last 
manager that uses it, and forgets them. A single endpoint (or repository) still 
closes all three when it stops, as before. A cache given to the 
`JCacheManager(Cache)` constructor is still not closed.
   - A closed manager does not open its cache again: `getCache()` throws 
`IllegalStateException` (an endpoint creates a new manager each time it starts, 
and so do the repositories). This is the case of routes that use the same 
jcache uri, which share one endpoint and so one manager: removing the route 
that consumes from `jcache://orders` stops that endpoint while the route that 
sends to `jcache://orders` still uses it. Its producer fails, as on main (where 
the manager handed out the closed cache). Without this, its `getCache()` would 
open the cache again, count it, and nothing would ever release it, since the 
endpoint is already stopped; with the provider closed it would even be a new, 
empty cache. This PR fixes the endpoints of other uris and the repositories; 
the shared endpoint of one uri is the core's "stops the endpoint of a removed 
route" below. (camel-ehcache, CAMEL-26302, goes further for the same case: 
there the producers and consumers hold a reference on the shared cache ma
 nager themselves. Doing that here means counting per producer and consumer 
instead of per `JCacheManager`; I can do it in a follow-up JIRA if you prefer 
it here too.)
   - `JCacheConsumer` deregisters its listener on the cache it registered it 
on, and not on a closed cache, instead of asking the manager of the endpoint 
for the cache again: that manager may be closed when the consumer stops (a 
route that never started is removed from the same uri, which stops the 
endpoint), and `getCache()` of a closed manager now throws. The running 
consumer of that uri gets no more events once the endpoint stopped and its 
cache was closed, as on main.
   - Each manager remembers what it counted and releases only that. A manager 
whose `getCache()` failed after it got the provider and the cache manager (the 
cache does not exist and `createCacheIfNotExists=false`) counted nothing: its 
`close()` closes them only when no other manager uses them, as on main when 
nobody else does. Without this, stopping the endpoint of a route whose cache 
did not exist took one use away from the other endpoints, or closed their cache 
manager.
   - Only Camel's `JCacheManager`s are counted. Other users of the same 
JVM-wide provider, such as application code or `JCachePolicy` (which takes 
`Caching.getCachingProvider().getCacheManager()` and never closes it), still 
see it closed when the last jcache endpoint or repository stops, as on main at 
every endpoint stop. Camel cannot tell them apart from its own use; the 
endpoint options name a provider class, not a provider instance, so no provider 
or cache manager instance set by the user is closed.
   - No option, generated file or upgrade guide change.
   - Backport: camel-4.22.x, 4.18.x and 4.14.x have the same 
`JCacheManager.close()` (read, not run).
   
   Tests:
   - New `JCacheRemoveRouteTest` (the module's Hazelcast JCache provider), 5 
tests; the first three fail on main (3 runs):
     - `removingARouteKeepsTheCachesOfTheOtherRoutes` and 
`removingTheConsumerRouteOfACacheKeepsItsProducer`: `IllegalStateException: 
Cache operations can not be performed. The cache closed`.
     - `theLastManagerOfACacheClosesIt`: two managers of one cache; after the 
first closes, the cache must still be open (main: `expected: <false> but was: 
<true>`), and after the second it must be closed.
     - `removingTheConsumerRouteOfTheSameUriStopsTheEndpointOfItsProducer`: 
routes from and to the same uri; after the consumer route is removed, a send 
fails with an `IllegalStateException`. It fails with the first version of this 
change, which opened the cache again (the send succeeded).
   - New `JCacheManagerTest.aClosedManagerDoesNotOpenItsCacheAgain`; fails with 
the first version of this change.
   - 
`JCacheRemoveRouteTest#consumerStopsAfterTheEndpointWasStoppedWithAnotherRoute`:
 an `autoStartup(false)` route on the uri of a running consumer route is 
removed, then the running route is stopped. Without the `JCacheConsumer` change 
(the consumer asks the closed manager for its cache) the stop fails.
   - New 
`JCacheManagerTest.aManagerThatCouldNotGetItsCacheDoesNotCloseTheCacheManagerOfTheOthers`:
 a manager with `createCacheIfNotExists=false` for a missing cache is closed 
while another manager uses the same cache manager; the other cache must stay 
open. Fails on main and with a count that does not remember what each manager 
counted (`expected: <false> but was: <true>`).
   - camel-jcache: 98 tests, 0 failures. The formatter and import-sort plugins 
ran.
   
   How it was found: a code sweep for endpoints whose `doStop` releases 
something that other endpoints still use, after a random lifecycle-sequence 
harness found the same kind of defect in camel-disruptor.
   
   
   Not changed: the core stops the endpoint of a removed route's consumer even 
when other routes still use it. Other components whose endpoint `doStop` 
releases something the other routes of the same uri still use may have the same 
problem; I can file a follow-up JIRA for the ones I can reproduce.
   
   # Target
   
   - [x] I checked that the commit is targeting the correct branch (Camel 4 
uses the `main` branch)
   
   # Tracking
   - [x] If this is a large change, bug fix, or code improvement, I checked 
there is a [JIRA issue](https://issues.apache.org/jira/browse/CAMEL-26287) 
filed for the change (usually before you start working on it): 
[CAMEL-26287](https://issues.apache.org/jira/browse/CAMEL-26287).
   
   # Apache Camel coding standards and style
   
   - [x] I checked that each commit in the pull request has a meaningful 
subject line and body.
   - [ ] I have run `mvn clean install -DskipTests` locally from root folder 
and I have committed all auto-generated changes.
     (I built camel-jcache and ran its tests, including the formatter and 
import-sort plugins. No generated file changes. I did not run the full root 
build.)
   
   # AI-assisted contributions
   
   - [x] If this PR includes AI-generated code, commits have proper 
co-authorship attribution (e.g., `Co-authored-by` trailers) and the PR 
description identifies the AI tool used.
     This PR was prepared with Claude Code (Claude Opus 5.5). The commit 
carries a `Co-Authored-By` trailer.
   
   _Claude Code on behalf of allthingssecurity_
   
   🤖 Generated with [Claude Code](https://claude.com/claude-code)
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to