[ 
https://issues.apache.org/jira/browse/CAMEL-26645?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Serdar Gökay reassigned CAMEL-26645:
------------------------------------

    Assignee: Serdar Gökay

> camel-core - Supervising route controller: route consumers do not get the 
> application class loader as TCCL, so camel-mail loses attachments in camel run
> --------------------------------------------------------------------------------------------------------------------------------------------------------
>
>                 Key: CAMEL-26645
>                 URL: https://issues.apache.org/jira/browse/CAMEL-26645
>             Project: Camel
>          Issue Type: Bug
>          Components: camel-core
>    Affects Versions: 4.23.0
>            Reporter: Serdar Gökay
>            Assignee: Serdar Gökay
>            Priority: Minor
>
> With the supervising route controller enabled, the threads of route consumers 
> do not have the application context class loader as their thread context 
> class loader (TCCL). With the controller off they normally do. Everything 
> that looks up services through the TCCL on those threads then sees only the 
> parent class path.
> The visible case: under {{camel run}}, camel-mail silently loses attachments. 
> The IMAP consumer gets the message, the body is a raw stream, and the 
> attachment list is empty. Nothing is logged.
> h3. Reproduce
> Camel 4.23.0 CLI, Java 25, GreenMail 2.1.15 standalone 
> ({{com.icegreen:greenmail-standalone}}) as the mail server.
> {noformat}
> java -Dgreenmail.setup.test.all -Dgreenmail.users=test:test@localhost 
> -Dgreenmail.hostname=127.0.0.1 -jar greenmail-standalone-2.1.15.jar
> {noformat}
> mail.camel.yaml:
> {noformat}
> - route:
>     from:
>       uri: imap://localhost:3143
>       parameters:
>         username: test
>         password: test
>         delay: 1000
>         delete: false
>         unseen: true
>       steps:
>         - log: "bodyType=${bodyType} attachments=${attachments.size}"
> {noformat}
> mail.eml:
> {noformat}
> From: a@localhost
> To: test@localhost
> Subject: with-attachment
> MIME-Version: 1.0
> Content-Type: multipart/mixed; boundary="b1"
> --b1
> Content-Type: text/plain; charset=UTF-8
> hello
> --b1
> Content-Type: application/octet-stream; name="orders.bin"
> Content-Disposition: attachment; filename="orders.bin"
> Content-Transfer-Encoding: base64
> UEsDBAo=
> --b1--
> {noformat}
> Send the mail and run the route, once per setting:
> {noformat}
> curl --url smtp://127.0.0.1:3025 --mail-from a@localhost --mail-rcpt 
> test@localhost --upload-file mail.eml
> camel run mail.camel.yaml --prop=camel.routecontroller.enabled=false
>   bodyType=class jakarta.mail.internet.MimeMultipart attachments=1
> curl --url smtp://127.0.0.1:3025 --mail-from a@localhost --mail-rcpt 
> test@localhost --upload-file mail.eml
> camel run mail.camel.yaml --prop=camel.routecontroller.enabled=true
>   bodyType=class org.apache.camel.converter.stream.InputStreamCache 
> attachments=0
> {noformat}
> h3. What the threads see
> Same mail route on 4.23.0, printing 
> {{Thread.currentThread().getContextClassLoader()}} on the consumer thread. A 
> timer route in the same runs shows the same class loader as the mail route, 
> so this is not specific to camel-mail.
> ||Route controller||Route started on||TCCL on the consumer 
> thread||Attachments||
> |off|main|DependencyDownloaderClassLoader|1|
> |on, initialDelay=0|main|AppClassLoader|0|
> |on, initialDelay=1000|controller thread|AppClassLoader|0|
> |on, route in excludeRoutes|main|AppClassLoader|0|
> h3. Cause
> A consumer creates its threads when it is started, and they inherit the TCCL 
> of the thread starting the route. {{AbstractCamelContext}} sets the 
> application class loader as TCCL in {{doLifecycleChange()}}, which covers 
> {{doStart()}}, so routes started there are fine. 
> {{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. The 
> second row shows the thread is not the point: the route starts on main and 
> still gets the wrong TCCL.
> * camel-kafka creates its consumer threads from that callback. Their TCCL is 
> wrong even with the controller off.
> A route started with {{getRouteController().startRoute(id)}} after the 
> context has started has the same gap, whatever the controller: it gets the 
> TCCL of the calling thread.
> For camel-mail: the jakarta.activation API is on the application class path, 
> while the Angus provider and mail handlers are downloaded. With the wrong 
> TCCL, {{MailcapCommandMap}} finds no {{MailcapRegistryProvider}} and no 
> handler for multipart, so {{Message.getContent()}} returns the raw stream, 
> and {{MailBinding.extractAttachmentsFromMail}} sees no {{Multipart}}.
> h3. Related
> * CAMEL-22476 is the same cause in camel-plc4x, solved inside that component 
> by passing the application class loader to its driver manager.
> * CAMEL-8339 set the TCCL in {{MailConfiguration}} and {{MailProducer}} for 
> the mail session, which is why sending works. It does not cover reading.
> * {{RouteOnDemandReloadStrategy}} sets the application class loader as TCCL 
> for route reloads.
> h3. Proposed change
> Use the scope {{AbstractCamelContext}} already has, {{doLifecycleChange()}}, 
> in two more places: in 
> {{InternalRouteStartupManager.safelyStartRouteServices}}, which every route 
> start passes through, and around the {{onCamelContextFullyStarted}} loop in 
> {{AbstractCamelContext.start()}}. It restores the previous TCCL afterwards 
> and changes nothing when no application class loader is set.
> With this change on 4.23.0, all four rows above give 
> DependencyDownloaderClassLoader and attachments=1. On main, the KafkaConsumer 
> threads of camel-kafka get the application class loader in both modes, and 
> new unit tests fail without the change and pass with it. A PR follows.
> _Claude Code on behalf of etzel-mes_



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

Reply via email to