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