Details
-
Bug
-
Status: Resolved
-
Minor
-
Resolution: Fixed
-
3.7.1
-
None
-
Unknown
Description
Bean reference by name (#bean:name syntax) in properties not working when there are custom converters registered in Spring Boot Application. I use Spring Boot 2.4.2 and Camel 3.7.1 to create Quartz component with manally defined scheduler via property:
camel.component.quartz.scheduler=#quartzScheduler
This causes error:
Failed to bind properties under 'camel.component.quartz.scheduler' to org.quartz.Scheduler:
Property: camel.component.quartz.scheduler
Value: #quartzScheduler
Origin: class path resource [application.properties] - 93:34
Reason: No converter found capable of converting from type [java.lang.String] to type[org.quartz.Scheduler]
Previously, in Spring Boot 2.3.0 and Camel 3.3.0 it worked ok.
Debugging shows that problem is caused by the fact that:
- QuartzComponentConverter registers itself only in ApplicationConversionService.getSharedInstance
- QuartzComponentConverter is not a bean in Spring context
- org.springframework.boot.context.properties.ConversionServiceDeducer.Factory.create() creates separate instance of ApplicationConversionService if there are any custom converters (which indeed are present in application configuration)
Quick workaround is to add custom property converter similar to QuartzComponentConverter that will be picked by the ConversionServiceDeducer:
@ConfigurationPropertiesBinding @Component public class QuartzComponentPropertyConverter implements GenericConverter { @Autowired private ApplicationContext applicationContext; public Set<ConvertiblePair> getConvertibleTypes() { Set<ConvertiblePair> answer = new LinkedHashSet<>(); answer.add(new ConvertiblePair(String.class, org.quartz.Scheduler.class)); answer.add(new ConvertiblePair(String.class, org.quartz.SchedulerFactory.class)); return answer; } public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { if (source == null) { return null; } String ref = source.toString(); if (!ref.startsWith("#")) { return null; } ref = ref.startsWith("#bean:") ? ref.substring(6) : ref.substring(1); switch (targetType.getName()) { case "org.quartz.Scheduler": return applicationContext.getBean(ref, org.quartz.Scheduler.class); case "org.quartz.SchedulerFactory": return applicationContext.getBean(ref, org.quartz.SchedulerFactory.class); } return null; } }