Croway commented on PR #1995:
URL: 
https://github.com/apache/camel-spring-boot/pull/1995#issuecomment-5812009239

   Follow-up to my previous comment: I found how the `JacksonFeatureSet` bind 
failure happens even when **no** `ObjectMapper` property is set.
   
   **Trigger: a non-enumerable `PropertySource` in the environment.** Spring 
Boot can't list the keys of such a source, so it can't rule out that nested 
keys under `camel.component.salesforce.*` exist. The binder then:
   1. creates a `SalesforceEndpointConfig` for `config`;
   2. walks its `ObjectMapper` down to 
`serializer-provider.generator.write-capabilities`;
   3. fails there with `InstantiationException`.
   
   On current `main` (Spring Boot 4.1.1), with no salesforce properties set at 
all, registering one empty non-enumerable source is enough to reproduce the 
exact error:
   
   ```
   Failed to bind properties under 
'camel.component.salesforce.config.object-mapper.serializer-provider.generator.write-capabilities'
     to 
com.fasterxml.jackson.core.util.JacksonFeatureSet<com.fasterxml.jackson.core.StreamWriteCapability>
   root: java.lang.InstantiationException
   ```
   
   So the problem is real, and I was wrong earlier to suggest it only happens 
with explicitly set keys. The concerns about the current approach still apply, 
though: turning every `object`/`duration` option into `String` silently drops 
normal nested settings such as `config.api-version` and 
`config.object-mapper=#bean:…`, and it changes public setter types across all 
starters.
   
   **Narrower fix: not obvious yet.** The obvious narrowing doesn't work. That 
would be keeping nested binding for Camel configuration classes and making only 
external types like `ObjectMapper` a `String`. But `SalesforceEndpointConfig` 
has its own `ObjectMapper` field, so walking into `config` still reaches the 
failing path. Spring Boot has no per-field "don't walk into this" switch for 
non-`java.*` types. Directions worth prototyping:
   - keep the real types, but stop the generated configuration class from 
exposing external, non-bean types (e.g. Jackson's) as walkable JavaBean 
properties, including inside nested config classes, while still accepting 
`#bean:` references;
   - or limit the `String` mapping to options whose type is not a Camel 
configuration class, and handle external-typed fields inside nested config 
classes separately in the generator.
   
   Both need checking against the existing binding tests (`#bean:` references, 
nested `config.*`/`http-configuration.*` keys) before they're a real proposal. 
I'll share results once I have a prototype. In the meantime, do you know which 
non-enumerable property source is registered where you see the failure? If it 
comes from a specific library or setup, that's useful context for the fix and 
its test.
   
   <details><summary>Reproducer (no salesforce properties, one non-enumerable 
source; uses the <code>App</code> config from the previous reproducer)</summary>
   
   ```java
   package org.apache.camel.component.salesforce.springboot;
   
   import org.junit.jupiter.api.Test;
   import org.springframework.boot.WebApplicationType;
   import org.springframework.boot.builder.SpringApplicationBuilder;
   import org.springframework.context.ConfigurableApplicationContext;
   import org.springframework.core.env.PropertySource;
   
   /**
    * No salesforce property set at all, but the environment has a 
non-enumerable PropertySource (as e.g. vault / cloud
    * config / custom resolvers register), so the Binder cannot rule out 
descendants and walks into the ObjectMapper.
    */
   public class SalesforceNonEnumerableSourceReproducerTest {
   
       @Test
       void startup() {
           SpringApplicationBuilder b = new 
SpringApplicationBuilder(SalesforceObjectMapperBindingReproducerTest.App.class)
                   .web(WebApplicationType.NONE)
                   .initializers(ctx -> 
ctx.getEnvironment().getPropertySources().addLast(new 
PropertySource<Object>("nonEnumerable") {
                       @Override
                       public Object getProperty(String name) {
                           return null;
                       }
                   }));
           try (ConfigurableApplicationContext ctx = b.run()) {
               System.out.println("REPRO2 -> STARTED");
           } catch (Exception e) {
               Throwable root = e;
               while (root.getCause() != null) {
                   root = root.getCause();
               }
               String msg = String.valueOf(e.getMessage());
               int i = msg.indexOf("Failed to bind properties under");
               System.out.println("REPRO2 -> FAILED " + (i >= 0 ? 
msg.substring(i, Math.min(msg.length(), i + 250)) : msg) + " | root: " + root);
           }
       }
   }
   ```
   </details>
   
   _Claude Code on behalf of Croway_
   


-- 
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