This is an automated email from the ASF dual-hosted git repository.

davsclaus 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 50ad0951278 CAMEL-19924: camel-jbang - Java compiled classes should be 
available for classloading by components. There are some other fixes for 
loading OSGi blueprint <beans> and camel-mybatis as well. (#11587)
50ad0951278 is described below

commit 50ad0951278b04601475101233625b12638317e0
Author: Claus Ibsen <[email protected]>
AuthorDate: Wed Sep 27 17:04:55 2023 +0200

    CAMEL-19924: camel-jbang - Java compiled classes should be available for 
classloading by components. There are some other fixes for loading OSGi 
blueprint <beans> and camel-mybatis as well. (#11587)
---
 .../component/mybatis/BaseMyBatisEndpoint.java     | 14 +++++-
 .../camel/component/mybatis/MyBatisComponent.java  | 28 +++++------
 .../component/mybatis/MyBatisTestSupport.java      |  2 +-
 .../java/org/apache/camel/spi/ClassResolver.java   |  9 +++-
 .../camel/impl/engine/DefaultClassResolver.java    | 10 ++++
 .../camel/dsl/java/joor/JavaJoorClassLoader.java   |  8 +++-
 .../java/org/apache/camel/main/KameletMain.java    | 16 +++----
 .../main/download/CamelCustomClassLoader.java}     | 33 +++++++------
 .../xml/blueprint/BlueprintXmlBeansHandler.java    | 56 +++++++++++++++++++---
 .../main/xml/spring/SpringXmlBeansHandler.java     |  4 ++
 10 files changed, 130 insertions(+), 50 deletions(-)

diff --git 
a/components/camel-mybatis/src/main/java/org/apache/camel/component/mybatis/BaseMyBatisEndpoint.java
 
b/components/camel-mybatis/src/main/java/org/apache/camel/component/mybatis/BaseMyBatisEndpoint.java
index 7c65469eee8..5d521584ddd 100644
--- 
a/components/camel-mybatis/src/main/java/org/apache/camel/component/mybatis/BaseMyBatisEndpoint.java
+++ 
b/components/camel-mybatis/src/main/java/org/apache/camel/component/mybatis/BaseMyBatisEndpoint.java
@@ -23,6 +23,9 @@ import org.apache.ibatis.session.ExecutorType;
 import org.apache.ibatis.session.SqlSessionFactory;
 
 public abstract class BaseMyBatisEndpoint extends DefaultPollingEndpoint {
+
+    private SqlSessionFactory sqlSessionFactory;
+
     @UriParam(label = "producer", defaultValue = "SIMPLE")
     private ExecutorType executorType;
     @UriParam(label = "producer")
@@ -42,8 +45,17 @@ public abstract class BaseMyBatisEndpoint extends 
DefaultPollingEndpoint {
         return (MyBatisComponent) super.getComponent();
     }
 
+    @Override
+    protected void doStart() throws Exception {
+        super.doStart();
+
+        if (sqlSessionFactory == null) {
+            sqlSessionFactory = getComponent().createSqlSessionFactory();
+        }
+    }
+
     public SqlSessionFactory getSqlSessionFactory() {
-        return getComponent().getSqlSessionFactory();
+        return sqlSessionFactory;
     }
 
     public ExecutorType getExecutorType() {
diff --git 
a/components/camel-mybatis/src/main/java/org/apache/camel/component/mybatis/MyBatisComponent.java
 
b/components/camel-mybatis/src/main/java/org/apache/camel/component/mybatis/MyBatisComponent.java
index 0eab5409740..e8112e436a4 100644
--- 
a/components/camel-mybatis/src/main/java/org/apache/camel/component/mybatis/MyBatisComponent.java
+++ 
b/components/camel-mybatis/src/main/java/org/apache/camel/component/mybatis/MyBatisComponent.java
@@ -34,7 +34,7 @@ import org.apache.ibatis.session.SqlSessionFactoryBuilder;
 public class MyBatisComponent extends HealthCheckComponent {
 
     @Metadata(label = "advanced", autowired = true)
-    private SqlSessionFactory sqlSessionFactory;
+    private volatile SqlSessionFactory sqlSessionFactory;
     @Metadata(defaultValue = "SqlMapConfig.xml", supportFileReference = true)
     private String configurationUri = "SqlMapConfig.xml";
 
@@ -45,14 +45,17 @@ public class MyBatisComponent extends HealthCheckComponent {
         return answer;
     }
 
-    protected SqlSessionFactory createSqlSessionFactory() throws IOException {
-        ObjectHelper.notNull(configurationUri, "configurationUri", this);
-        InputStream is = 
ResourceHelper.resolveMandatoryResourceAsInputStream(getCamelContext(), 
configurationUri);
-        try {
-            return new SqlSessionFactoryBuilder().build(is);
-        } finally {
-            IOHelper.close(is);
+    protected synchronized SqlSessionFactory createSqlSessionFactory() throws 
IOException {
+        if (sqlSessionFactory == null) {
+            ObjectHelper.notNull(configurationUri, "configurationUri", this);
+            InputStream is = 
ResourceHelper.resolveMandatoryResourceAsInputStream(getCamelContext(), 
configurationUri);
+            try {
+                sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
+            } finally {
+                IOHelper.close(is);
+            }
         }
+        return sqlSessionFactory;
     }
 
     public SqlSessionFactory getSqlSessionFactory() {
@@ -79,13 +82,4 @@ public class MyBatisComponent extends HealthCheckComponent {
         this.configurationUri = configurationUri;
     }
 
-    @Override
-    protected void doStart() throws Exception {
-        super.doStart();
-
-        if (sqlSessionFactory == null) {
-            sqlSessionFactory = createSqlSessionFactory();
-        }
-    }
-
 }
diff --git 
a/components/camel-mybatis/src/test/java/org/apache/camel/component/mybatis/MyBatisTestSupport.java
 
b/components/camel-mybatis/src/test/java/org/apache/camel/component/mybatis/MyBatisTestSupport.java
index 05bdc2cdbf5..d71a3ba87cd 100644
--- 
a/components/camel-mybatis/src/test/java/org/apache/camel/component/mybatis/MyBatisTestSupport.java
+++ 
b/components/camel-mybatis/src/test/java/org/apache/camel/component/mybatis/MyBatisTestSupport.java
@@ -95,7 +95,7 @@ public abstract class MyBatisTestSupport extends 
CamelTestSupport {
 
     protected Connection createConnection() throws Exception {
         MyBatisComponent component = context.getComponent("mybatis", 
MyBatisComponent.class);
-        return 
component.getSqlSessionFactory().getConfiguration().getEnvironment().getDataSource().getConnection();
+        return 
component.createSqlSessionFactory().getConfiguration().getEnvironment().getDataSource().getConnection();
     }
 
 }
diff --git 
a/core/camel-api/src/main/java/org/apache/camel/spi/ClassResolver.java 
b/core/camel-api/src/main/java/org/apache/camel/spi/ClassResolver.java
index f7748d7eb3e..8c0f573bf2d 100644
--- a/core/camel-api/src/main/java/org/apache/camel/spi/ClassResolver.java
+++ b/core/camel-api/src/main/java/org/apache/camel/spi/ClassResolver.java
@@ -18,11 +18,13 @@ package org.apache.camel.spi;
 
 import java.io.InputStream;
 import java.net.URL;
+import java.util.Collection;
 import java.util.Enumeration;
+import java.util.Set;
 
 /**
  * A class resolver for loading classes in a loosly coupled manner to cater 
for different platforms such as standalone,
- * web container, j2ee container and OSGi platforms.
+ * Spring Boot, Quarkus, JBang etc.
  */
 public interface ClassResolver {
 
@@ -33,6 +35,11 @@ public interface ClassResolver {
      */
     void addClassLoader(ClassLoader classLoader);
 
+    /**
+     * Gets the custom class loaders.
+     */
+    Set<ClassLoader> getClassLoaders();
+
     /**
      * Resolves the given class by its name
      *
diff --git 
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultClassResolver.java
 
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultClassResolver.java
index 14f4cc89ff5..a047df98724 100644
--- 
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultClassResolver.java
+++ 
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultClassResolver.java
@@ -18,6 +18,7 @@ package org.apache.camel.impl.engine;
 
 import java.io.InputStream;
 import java.net.URL;
+import java.util.Collections;
 import java.util.Enumeration;
 import java.util.LinkedHashSet;
 import java.util.Set;
@@ -61,6 +62,15 @@ public class DefaultClassResolver implements ClassResolver, 
CamelContextAware {
         classLoaders.add(classLoader);
     }
 
+    @Override
+    @SuppressWarnings("unchecked")
+    public Set<ClassLoader> getClassLoaders() {
+        if (classLoaders == null) {
+            return Collections.EMPTY_SET;
+        }
+        return Collections.unmodifiableSet(classLoaders);
+    }
+
     @Override
     public Class<?> resolveClass(String name) {
         Class<?> answer;
diff --git 
a/dsl/camel-java-joor-dsl/src/main/java/org/apache/camel/dsl/java/joor/JavaJoorClassLoader.java
 
b/dsl/camel-java-joor-dsl/src/main/java/org/apache/camel/dsl/java/joor/JavaJoorClassLoader.java
index 95a5d6f3257..f83ba0953f7 100644
--- 
a/dsl/camel-java-joor-dsl/src/main/java/org/apache/camel/dsl/java/joor/JavaJoorClassLoader.java
+++ 
b/dsl/camel-java-joor-dsl/src/main/java/org/apache/camel/dsl/java/joor/JavaJoorClassLoader.java
@@ -28,8 +28,12 @@ public class JavaJoorClassLoader extends ClassLoader {
     }
 
     @Override
-    protected Class<?> findClass(String name) {
-        return classes.get(name);
+    public Class<?> loadClass(String name) throws ClassNotFoundException {
+        Class<?> clazz = classes.get(name);
+        if (clazz != null) {
+            return clazz;
+        }
+        throw new ClassNotFoundException(name);
     }
 
     public void addClass(String name, Class<?> clazz) {
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 c436be5703f..39a4793b899 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
@@ -34,6 +34,7 @@ import org.apache.camel.dsl.support.SourceLoader;
 import org.apache.camel.impl.DefaultCamelContext;
 import org.apache.camel.main.download.AutoConfigureDownloadListener;
 import org.apache.camel.main.download.BasePackageScanDownloadListener;
+import org.apache.camel.main.download.CamelCustomClassLoader;
 import org.apache.camel.main.download.CircuitBreakerDownloader;
 import org.apache.camel.main.download.CommandLineDependencyDownloader;
 import org.apache.camel.main.download.DependencyDownloaderClassLoader;
@@ -325,6 +326,8 @@ public class KameletMain extends MainCommandLineSupport {
         if (getCamelContext() != null) {
             getCamelContext().stop();
         }
+        springXmlBeansHandler.stop();
+        blueprintXmlBeansHandler.stop();
     }
 
     @Override
@@ -343,7 +346,7 @@ public class KameletMain extends MainCommandLineSupport {
         // do not build/init camel context yet
         DefaultCamelContext answer = new DefaultCamelContext(false);
         if (download) {
-            ClassLoader dynamicCL = createApplicationContextClassLoader();
+            ClassLoader dynamicCL = 
createApplicationContextClassLoader(answer);
             answer.setApplicationContextClassLoader(dynamicCL);
             
PluginHelper.getPackageScanClassResolver(answer).addClassLoader(dynamicCL);
             
PluginHelper.getPackageScanResourceResolver(answer).addClassLoader(dynamicCL);
@@ -566,8 +569,9 @@ public class KameletMain extends MainCommandLineSupport {
 
     @Override
     protected void autoconfigure(CamelContext camelContext) throws Exception {
+        ClassLoader cl = createApplicationContextClassLoader(camelContext);
         // create classloader that may include additional JARs
-        
camelContext.setApplicationContextClassLoader(createApplicationContextClassLoader());
+        camelContext.setApplicationContextClassLoader(cl);
         // auto configure camel afterwards
         super.autoconfigure(camelContext);
     }
@@ -577,7 +581,7 @@ public class KameletMain extends MainCommandLineSupport {
         return new KameletAutowiredLifecycleStrategy(camelContext, 
stubPattern, silent);
     }
 
-    protected ClassLoader createApplicationContextClassLoader() {
+    protected ClassLoader createApplicationContextClassLoader(CamelContext 
camelContext) {
         if (classLoader == null) {
             // jars need to be added to dependency downloader classloader
             List<String> jars = new ArrayList<>();
@@ -600,6 +604,7 @@ public class KameletMain extends MainCommandLineSupport {
                     LOG.info("Additional files added to classpath: {}", 
String.join(", ", files));
                 }
             }
+            parentCL = new CamelCustomClassLoader(parentCL, camelContext);
             DependencyDownloaderClassLoader cl = new 
DependencyDownloaderClassLoader(parentCL);
             if (!jars.isEmpty()) {
                 for (String jar : jars) {
@@ -688,11 +693,6 @@ public class KameletMain extends MainCommandLineSupport {
         blueprintXmlBeansHandler.createAndRegisterBeans(camelContext);
     }
 
-    @Override
-    protected void doShutdown() throws Exception {
-        // TODO: manage BeanFactory as a field and clear the beans here
-    }
-
     private static String getPid() {
         return String.valueOf(ProcessHandle.current().pid());
     }
diff --git 
a/dsl/camel-java-joor-dsl/src/main/java/org/apache/camel/dsl/java/joor/JavaJoorClassLoader.java
 
b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/CamelCustomClassLoader.java
similarity index 50%
copy from 
dsl/camel-java-joor-dsl/src/main/java/org/apache/camel/dsl/java/joor/JavaJoorClassLoader.java
copy to 
dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/CamelCustomClassLoader.java
index 95a5d6f3257..5018b9818b3 100644
--- 
a/dsl/camel-java-joor-dsl/src/main/java/org/apache/camel/dsl/java/joor/JavaJoorClassLoader.java
+++ 
b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/CamelCustomClassLoader.java
@@ -14,28 +14,33 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.dsl.java.joor;
+package org.apache.camel.main.download;
 
-import java.util.HashMap;
-import java.util.Map;
+import org.apache.camel.CamelContext;
 
-public class JavaJoorClassLoader extends ClassLoader {
+/**
+ * ClassLoader loading from any custom class loaders that may
+ * have been added to Camel {@link org.apache.camel.spi.ClassResolver}.
+ */
+public class CamelCustomClassLoader extends ClassLoader {
 
-    private final Map<String, Class<?>> classes = new HashMap<>();
+    private final CamelContext camelContext;
 
-    public JavaJoorClassLoader() {
-        super(JavaJoorClassLoader.class.getClassLoader());
+    public CamelCustomClassLoader(ClassLoader parent, CamelContext 
camelContext) {
+        super(parent);
+        this.camelContext = camelContext;
     }
 
     @Override
-    protected Class<?> findClass(String name) {
-        return classes.get(name);
-    }
-
-    public void addClass(String name, Class<?> clazz) {
-        if (name != null && clazz != null) {
-            classes.put(name, clazz);
+    protected Class<?> findClass(String name) throws ClassNotFoundException {
+        for (ClassLoader cl : 
camelContext.getClassResolver().getClassLoaders()) {
+            try {
+                return cl.loadClass(name);
+            } catch (ClassNotFoundException e) {
+                // ignore
+            }
         }
+        throw new ClassNotFoundException(name);
     }
 
 }
diff --git 
a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/blueprint/BlueprintXmlBeansHandler.java
 
b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/blueprint/BlueprintXmlBeansHandler.java
index c5037814ebb..4be053ce16e 100644
--- 
a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/blueprint/BlueprintXmlBeansHandler.java
+++ 
b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/blueprint/BlueprintXmlBeansHandler.java
@@ -36,7 +36,9 @@ import org.apache.camel.model.Model;
 import org.apache.camel.model.app.RegistryBeanDefinition;
 import org.apache.camel.spi.Resource;
 import org.apache.camel.spi.ResourceLoader;
+import org.apache.camel.support.ObjectHelper;
 import org.apache.camel.support.PropertyBindingSupport;
+import org.apache.camel.util.KeyValueHolder;
 import org.apache.camel.util.StringHelper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -55,6 +57,7 @@ public class BlueprintXmlBeansHandler {
     private final Map<String, Node> delayedBeans = new LinkedHashMap<>();
     private final Map<String, Resource> resources = new LinkedHashMap<>();
     private final List<RegistryBeanDefinition> delayedRegistrations = new 
ArrayList<>();
+    private final Map<String, KeyValueHolder<Object, String>> beansToDestroy = 
new LinkedHashMap<>();
 
     /**
      * Parses the XML documents and discovers blueprint beans, which will be 
created manually via Camel.
@@ -118,7 +121,7 @@ public class BlueprintXmlBeansHandler {
         }
         String im = XmlHelper.getAttribute(node, "init-method");
         if (im != null) {
-            rrd.setInitMethod(fm);
+            rrd.setInitMethod(im);
         }
         String dm = XmlHelper.getAttribute(node, "destroy-method");
         if (dm != null) {
@@ -248,12 +251,8 @@ public class BlueprintXmlBeansHandler {
                 if (def.getProperties() != null && 
!def.getProperties().isEmpty()) {
                     PropertyBindingSupport.setPropertiesOnTarget(camelContext, 
target, def.getProperties());
                 }
-                camelContext.getRegistry().unbind(name);
-                camelContext.getRegistry().bind(name, target);
 
-                // register bean in model
-                Model model = 
camelContext.getCamelContextExtension().getContextPlugin(Model.class);
-                model.addRegistryBean(def);
+                bindBean(camelContext, def, name, target);
 
             } catch (Exception e) {
                 if (delayIfFailed) {
@@ -265,4 +264,49 @@ public class BlueprintXmlBeansHandler {
         }
     }
 
+    protected void bindBean(CamelContext camelContext, RegistryBeanDefinition 
def, String name, Object target)
+            throws Exception {
+        // destroy and unbind any existing bean
+        destroyBean(name, true);
+        camelContext.getRegistry().unbind(name);
+
+        // invoke init method and register bean
+        String initMethod = def.getInitMethod();
+        if (initMethod != null) {
+            ObjectHelper.invokeMethodSafe(initMethod, target);
+        }
+        camelContext.getRegistry().bind(name, target);
+
+        // remember to destroy bean on shutdown
+        if (def.getDestroyMethod() != null) {
+            beansToDestroy.put(name, new KeyValueHolder<>(target, 
def.getDestroyMethod()));
+        }
+
+        // register bean in model
+        Model model = 
camelContext.getCamelContextExtension().getContextPlugin(Model.class);
+        model.addRegistryBean(def);
+    }
+
+    protected void destroyBean(String name, boolean remove) {
+        var holder = remove ? beansToDestroy.remove(name) : 
beansToDestroy.get(name);
+        if (holder != null) {
+            String destroyMethod = holder.getValue();
+            Object target = holder.getKey();
+            try {
+                ObjectHelper.invokeMethodSafe(destroyMethod, target);
+            } catch (Exception e) {
+                LOG.warn("Error invoking destroy method: {} on bean: {} due 
to: {}. This exception is ignored.",
+                        destroyMethod, target, e.getMessage(), e);
+            }
+        }
+    }
+
+    public void stop() {
+        // beans should trigger destroy methods on shutdown
+        for (String name : beansToDestroy.keySet()) {
+            destroyBean(name, false);
+        }
+        beansToDestroy.clear();
+    }
+
 }
diff --git 
a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/spring/SpringXmlBeansHandler.java
 
b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/spring/SpringXmlBeansHandler.java
index 7fc231df481..22e1142975a 100644
--- 
a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/spring/SpringXmlBeansHandler.java
+++ 
b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/spring/SpringXmlBeansHandler.java
@@ -331,4 +331,8 @@ public class SpringXmlBeansHandler {
         return val;
     }
 
+    public void stop() {
+        // noop
+    }
+
 }

Reply via email to