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 <gr.grzy...@gmail.com>
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;
             }

Reply via email to