This is an automated email from the ASF dual-hosted git repository.
ggrzybek pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new 5c713226c88 [CAMEL-19564] Add 2nd attempt to instantiate spring beans
after reading class definitions from JavaRoutesBuilderLoader
5c713226c88 is described below
commit 5c713226c88f2d0ea58b7c0581e94d716b452c3d
Author: Grzegorz Grzybek <[email protected]>
AuthorDate: Fri Jun 30 14:13:14 2023 +0200
[CAMEL-19564] Add 2nd attempt to instantiate spring beans after reading
class definitions from JavaRoutesBuilderLoader
---
.../org/apache/camel/main/BaseMainSupport.java | 23 ++++--
.../java/org/apache/camel/main/KameletMain.java | 87 +++++++++++++++++++++-
2 files changed, 100 insertions(+), 10 deletions(-)
diff --git
a/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java
b/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java
index e6a12db2d85..aeb9df75289 100644
--- a/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java
+++ b/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java
@@ -657,6 +657,11 @@ public abstract class BaseMainSupport extends BaseService {
recorder.endStep(step);
}
+ // after the routes are read (org.apache.camel.spi.RoutesBuilderLoader
did their work), we may have
+ // new classes defined, so main implementations may have to
reconfigure the registry using newly
+ // available bean definitions
+ postProcessCamelRegistry(camelContext, mainConfigurationProperties);
+
// allow doing custom configuration before camel is started
for (MainListener listener : listeners) {
listener.afterConfigure(this);
@@ -896,8 +901,7 @@ public abstract class BaseMainSupport extends BaseService {
// Spring's ApplicationContext.
// so here, before configuring Camel Context, we can process the
registry and let Main implementations
// decide how to do it
- Registry registry =
camelContext.getCamelContextExtension().getRegistry();
- postProcessCamelRegistry(camelContext, config, registry);
+ preProcessCamelRegistry(camelContext, config);
// lookup and configure SPI beans
DefaultConfigurationConfigurer.afterConfigure(camelContext);
@@ -1152,11 +1156,18 @@ public abstract class BaseMainSupport extends
BaseService {
*
* @param camelContext
* @param config
- * @param registry
*/
- protected void postProcessCamelRegistry(
- CamelContext camelContext, MainConfigurationProperties config,
- Registry registry) {
+ protected void preProcessCamelRegistry(CamelContext camelContext,
MainConfigurationProperties config) {
+ }
+
+ /**
+ * Main implementation may do some additional configuration of the {@link
Registry} after loading the routes, but
+ * before the routes are started.
+ *
+ * @param camelContext
+ * @param config
+ */
+ protected void postProcessCamelRegistry(CamelContext camelContext,
MainConfigurationProperties config) {
}
private void setRouteTemplateProperties(
diff --git
a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/KameletMain.java
b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/KameletMain.java
index 65db3209e72..4a42b9eb3a2 100644
---
a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/KameletMain.java
+++
b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/KameletMain.java
@@ -21,6 +21,7 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -79,10 +80,16 @@ import
org.apache.camel.support.DefaultContextReloadStrategy;
import org.apache.camel.support.PluginHelper;
import org.apache.camel.support.RouteOnDemandReloadStrategy;
import org.apache.camel.support.service.ServiceHelper;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.CannotLoadBeanClassException;
+import org.springframework.beans.factory.SmartFactoryBean;
+import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.core.io.AbstractResource;
+import org.springframework.core.metrics.StartupStep;
/**
* A Main class for booting up Camel with Kamelet in standalone mode.
@@ -101,6 +108,11 @@ public class KameletMain extends MainCommandLineSupport {
private DownloadListener downloadListener;
private DependencyDownloaderClassLoader classLoader;
+ // when preparing spring-based beans, we may have problems loading classes
which are provided with Java DSL
+ // that's why some beans should be processed later
+ private final List<String> delayedBeans = new LinkedList<>();
+ private Set<String> infraBeanNames;
+
public KameletMain() {
configureInitialProperties(DEFAULT_KAMELETS_LOCATION);
}
@@ -607,7 +619,7 @@ public class KameletMain extends MainCommandLineSupport {
}
@Override
- protected void postProcessCamelRegistry(CamelContext camelContext,
MainConfigurationProperties config, Registry registry) {
+ protected void preProcessCamelRegistry(CamelContext camelContext,
MainConfigurationProperties config) {
// camel-kamelet-main has access to Spring libraries, so we can grab
XML documents representing
// actual Spring Beans and read them using Spring's BeanFactory to
populate Camel registry
final Map<String, Document> xmls = new TreeMap<>();
@@ -627,10 +639,12 @@ public class KameletMain extends MainCommandLineSupport {
// Spring registry and then copy the beans (whether the scope is)
final DefaultListableBeanFactory beanFactory = new
DefaultListableBeanFactory();
beanFactory.setAllowCircularReferences(true); // for now
+ beanFactory.setBeanClassLoader(classLoader);
+ registry.bind("SpringBeanFactory", beanFactory);
// register some existing beans (the list may change)
// would be nice to keep the documentation up to date:
docs/user-manual/modules/ROOT/pages/camel-jbang.adoc
- Set<String> infraBeanNames = Set.of("CamelContext",
"MainConfiguration");
+ infraBeanNames = Set.of("CamelContext", "MainConfiguration");
beanFactory.registerSingleton("CamelContext", camelContext);
beanFactory.registerSingleton("MainConfiguration", config);
// ...
@@ -658,9 +672,74 @@ public class KameletMain extends MainCommandLineSupport {
// which extra/infra beans are added
beanFactory.freezeConfiguration();
- beanFactory.preInstantiateSingletons();
- for (String name : beanFactory.getBeanDefinitionNames()) {
+ List<String> beanNames =
Arrays.asList(beanFactory.getBeanDefinitionNames());
+
+ // Trigger initialization of all non-lazy singleton beans...
+ instantiateAndRegisterBeans(beanFactory, beanNames);
+ }
+
+ @Override
+ protected void postProcessCamelRegistry(CamelContext camelContext,
MainConfigurationProperties config) {
+ if (delayedBeans.isEmpty()) {
+ return;
+ }
+
+ DefaultListableBeanFactory beanFactory
+ = registry.lookupByNameAndType("SpringBeanFactory",
DefaultListableBeanFactory.class);
+
+ // we have some beans with classes that we couldn't load before. now,
after loading the routes
+ // we may have the needed class definitions
+ for (String beanName : delayedBeans) {
+ BeanDefinition bd = beanFactory.getMergedBeanDefinition(beanName);
+ if (bd instanceof AbstractBeanDefinition abd) {
+ if (!abd.hasBeanClass()) {
+ Class<?> c =
camelContext.getClassResolver().resolveClass(abd.getBeanClassName());
+ abd.setBeanClass(c);
+ }
+ }
+ }
+
+ instantiateAndRegisterBeans(beanFactory, delayedBeans);
+ }
+
+ private void instantiateAndRegisterBeans(DefaultListableBeanFactory
beanFactory, List<String> beanNames) {
+ List<String> instantiatedBeanNames = new LinkedList<>();
+
+ for (String beanName : beanNames) {
+ BeanDefinition bd = beanFactory.getMergedBeanDefinition(beanName);
+ if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
+ try {
+ if (beanFactory.isFactoryBean(beanName)) {
+ Object bean =
beanFactory.getBean(BeanFactory.FACTORY_BEAN_PREFIX + beanName);
+ if (bean instanceof SmartFactoryBean<?>
smartFactoryBean && smartFactoryBean.isEagerInit()) {
+ beanFactory.getBean(beanName);
+ instantiatedBeanNames.add(beanName);
+ }
+ } else {
+ beanFactory.getBean(beanName);
+ instantiatedBeanNames.add(beanName);
+ }
+ } catch (CannotLoadBeanClassException ignored) {
+ // we'll try to resolve later
+ delayedBeans.add(beanName);
+ }
+ }
+ }
+
+ // Trigger post-initialization callback for all applicable beans...
+ for (String beanName : instantiatedBeanNames) {
+ Object singletonInstance = beanFactory.getSingleton(beanName);
+ if (singletonInstance instanceof SmartInitializingSingleton
smartSingleton) {
+ StartupStep smartInitialize =
beanFactory.getApplicationStartup()
+ .start("spring.beans.smart-initialize")
+ .tag("beanName", beanName);
+ smartSingleton.afterSingletonsInstantiated();
+ smartInitialize.end();
+ }
+ }
+
+ for (String name : instantiatedBeanNames) {
if (infraBeanNames.contains(name)) {
continue;
}