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]
