etzel-mes opened a new pull request, #29263: URL: https://github.com/apache/camel/pull/29263
# Description JIRA: CAMEL-26645 (filed by me as mardonius; it has the reproducer). **What goes wrong.** A consumer creates its threads when it is started, and they inherit the thread context class loader (TCCL) of the thread starting the route. `AbstractCamelContext` sets the application class loader as TCCL in `doLifecycleChange()`, which covers `doStart()`, so routes started there have it. `AbstractCamelContext.start()` calls `onCamelContextFullyStarted` after that scope has closed, and two things happen outside it: - The supervising route controller starts its routes from that callback. - camel-kafka creates its consumer threads from that callback, so its TCCL is wrong even with the controller off. A route started with `getRouteController().startRoute(id)` later on gets the TCCL of the calling thread, whatever the controller. Under `camel run` the downloaded libraries are visible only through the application class loader, so lookups through the TCCL on those threads see only the parent class path. The visible case is camel-mail with `camel.routecontroller.enabled=true`: the IMAP consumer gets the message, the body is a raw stream, the attachment list is empty, and nothing is logged. `MailcapCommandMap` finds no `MailcapRegistryProvider` and no handler for multipart, so `Message.getContent()` returns the stream. **The change.** `doLifecycleChange()`, the scope `AbstractCamelContext` already has, is used in two more places: - `InternalRouteStartupManager.safelyStartRouteServices`, which every route start passes through. - The `onCamelContextFullyStarted` loop in `AbstractCamelContext.start()`. It restores the previous TCCL afterwards and changes nothing when no application class loader is set. With the deprecated `useMDCLogging` on, these two places also get the context id in the MDC, as `doStart()` does. Why not in `MailConsumer`, as `MailProducer` does (CAMEL-8339): a timer route shows the same wrong TCCL, so that would fix one symptom only. CAMEL-22476 solved the same cause inside camel-plc4x by passing the application class loader to its driver manager; that code can stay. No upgrade guide entry: there is nothing to migrate, and these routes now see what routes started by the context already see. **Verification.** - `RouteStartApplicationClassLoaderTest`: 8 tests. On main, 6 fail: supervised route, with an initial delay, excluded from supervision, restarted after a failed start, started manually after the context started, and a listener of `onCamelContextFullyStarted`. The 2 that pass on main are the controls: a route started by the context, and no application class loader. - Each of the two places is needed: without the one in `safelyStartRouteServices` the manual start test fails; without the one in `start()` the listener test fails. - `*Supervising*Test`, `*RouteController*Test`, `*Startup*Test`, `RouteAutoStartupTest`, `RouteSuspendResume*Test`, `*MDC*Test`, `*Lifecycle*Test`, `*Reload*Test`, `DefaultCamelContext*Test` of camel-core with the new test: 219 tests, no failures, 3 skipped. The new test class five runs in a row with `-Dsurefire.rerunFailingTestsCount=0`. - camel-kafka (4.24.0-SNAPSHOT, no broker needed), reading the TCCL of its `KafkaConsumer` threads with the application class loader set: on main not the application class loader, with the controller on and off; with this change the application class loader in both. - End to end with `camel run` 4.23.0 with the same change applied, Java 25, GreenMail 2.1.15 as the IMAP server, and a timer route next to the mail route (same TCCL in each run): | Route controller | Route started on | TCCL before | Attachments before | TCCL with this change | Attachments with this change | | --- | --- | --- | --- | --- | --- | | off | main | DependencyDownloaderClassLoader | 1 | DependencyDownloaderClassLoader | 1 | | on, `initialDelay=0` | main | AppClassLoader | 0 | DependencyDownloaderClassLoader | 1 | | on, `initialDelay=1000` | controller thread | AppClassLoader | 0 | DependencyDownloaderClassLoader | 1 | | on, route in `excludeRoutes` | main | AppClassLoader | 0 | DependencyDownloaderClassLoader | 1 | - `-Psourcecheck` on camel-base-engine and camel-core; the formatter and impsort leave no changes. No root build. @davsclaus @gnodet could you have a look? (I cannot request reviewers.) # 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) filed for the change (usually before you start working on it). # 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. # 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. The code and tests were written with Claude Code (Claude Opus 5.5) under my direction; the commit carries the `Co-authored-by` trailer. _Claude Code on behalf of etzel-mes_ 🤖 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]
